一文带你看懂 Redis BitArray 如何实现高性能的位操作

本文涉及的产品
云原生内存数据库 Tair,内存型 2GB
云数据库 Redis 版,社区版 2GB
推荐场景:
搭建游戏排行榜
云数据库 Redis 版,经济版 1GB 1个月
简介: Redis 作为当代互联网行业无可替代的 Key-Value 数据库,在我们日常的工作中占据主要的角色,对于常用的命令相信大家都很熟悉。今天给大家分享一个平时可能用到的少,但是也很重要的一个类型 BitArray。我们先通过简单的命令使用,了解该命令的用法,然后再给大家介绍一下底层的实现原理,帮助大家更好的了解。

Redis 作为当代互联网行业无可替代的 Key-Value 数据库,在我们日常的工作中占据主要的角色,对于常用的命令相信大家都很熟悉。今天给大家分享一个平时可能用到的少,但是也很重要的一个类型 BitArray。我们先通过简单的命令使用,了解该命令的用法,然后再给大家介绍一下底层的实现原理,帮助大家更好的了解。

简单使用

我们先看下什么是BitArray 位数组。Redis 使用字符串对象来存储位数组,一个 Byte 字节有 8 个 bit 位,通过控制每一个 bit 位为 0 或者 1来表示某个元素对应的值或者状态。通过使用 8 个 bit 位可以对复杂操作节省很多的空间。BitArray 相关的操作命令有 SETBIT,GETBIT,BITCOUNT,BITOP。下面我们依次看下命令的使用,最后再看下实现的原理。

首先我们在本地启动一个 Redis 实例,再启动一个客户端去链接如下图,

6.jpg

通过redis-cli 链接客户端,执行相应的命令,接下来使用一下 BitArray 相关的命令,

7.jpg

通过setbit test 2 1 命令我们创建了一个名为 testbitarray 并将其第二位设置成 1,再使用getbit test 2 获取对应位的值。setbit命令功能是将对应的 key 指定 offset 的位置设置为 1 或 0,getbit 命令是获取指定 offset 位置的值。test 是一个位数组通过上面的命令值变成0000 0010

接下来我们再创建一个名为test2的位数组,并且通过多次使用 setbit 命令和 bitcountbitcount 命令的作用是用来统计位数组中 1 的个数,通过下面我们看到第一次使用 bitcount test2 命令时结果为 1,当使用了 setbit test2 1 1 命令后再次使用 bitcount 命令我们发现结果已经变成 2 了。其中test2 的刚开始是0000 0100 后面变成0000 0101

8.jpg

bitop 命令相信大家都能理解,都是一些与,或,异或,非的运算,就不赘述了,具体使用可以看上图。

原理

前面说到 Redis 是通过字符串对象来实现位数组的,所以字符串对象有的功能,在位数组上面都是有的,在Redis 底层位数组的存储结构也是基于 SDS (简单动态字符串)的,如下:

9.jpg

其中 len 字段表示包含的 buf 数组的个数,buf[i] 表示的是第i个字节数组里面具体的数值,buf[len] 是末尾的分隔符\0 。上图中的buf[0] 是一个字节,其中有 8 个 bit 位,在使用了 setbit 命令后初始值为0000 0000,buf[1] 中就是分隔符\0

SETBIT

当我们执行setbit key offset value  命令时,我们分两步:

  1. 计算出创建多少个字节数组(offset / 8) + 1;
  2. 判断是否长度不够需要进行扩容;
  3. 计算出 offset 对应的字节位置 byte = offset / 8;
  4. 计算出 offset 对应的 bit 位,bit = (offset mod 8) + 1;
  5. 根据 offset 找到对应的位置将此处的值改成value 并返回旧值;

假设我们执行的命令时setbit test2 3 1,第一步先计算字节个数 (3 / 8) + 1 = 1,计算出来我们只需要一个字节;第二步跟原始 len 进行比较,发现不需要扩容;3. 根据 offset 计算存放的字节 3 / 8 = 0 则,存放的 buf[0] 中;第四部计算 bit,( 3 mod 8) + 1 = 4,表示的是第四个 bit 位。经过一轮 test2 就变成了0000 1000

setbit 命令执行的操作都是常数级别的,时间复杂度为 O(1)。

GETBIT

我们知道的setbit 命令是如何实现的,那么getbit 命令也就知道如何计算了,过程是类似的。

  1. 找到对应的字节数组 byte = offset / 8;
  2. 计算出对应的 bit 位bit =  (offset mod 8) + 1;

经过上面的计算我们可以知道当执行命令 getbit test2 3  的时候,先算出 3 / 8 = 0 ,找到 buf[0],再使用(3 mod 8) + 1  = 4,找到 bit 位。

看到这里细心的小伙伴就会有疑问,会说不对啊,根据这个计算返回的值应该是 0 啊,因为上面 setbit命令执行完的结果是0000 1000 啊。

能发现这个问题的小伙伴说明很用心在看了,这里就要跟大家说下了,虽然 setbit 命令执行完结果是0000 1000 但是在 「buf[0] 中存储的确实反过来的,即为0001 0000。采用的是逆序的方式来保存位数组的。

之所以采用逆序保存位数组是为了减少位数组的移动,提高性能,感兴趣的小伙伴可以自行研究一下。

BITCOUNT 命令

bitcount 命令是用来计算一个位数组中 1 的个数,说起来比较简单,但是实现起来却很有讲究。我们设想一下,统计一个位数组中 1 的个数有多少个,最简单的办法就是遍历,依次累加。但是当我们的位数组很大的时候,整个效率就会变得非常慢,因为遍历是跟长度正相关的,当存放 100MB 的位数组整个遍历需要八亿次。而当达到 500MB 时整个遍历就达到了四十亿次!

在 Redis 中采用的是查表和 variable-precision SWAR 算法,查表是指当位数组长度小于 128 时,直接根据预设的映射表找到对应 1 的个数,直接返回。而variable-precision SWAR 算法相对比较复杂,阿粉也还要再研究研究,今天就先不分享了。

BITOP 命令

bitop 命令相对简单一点,因为 Redis 底层是基于 C 语言实现的,C语言本身就支持相关的逻辑运算。因为本身就是二进制位数组,所以对应的逻辑运算会简单很多就不赘述了,相信大家都能理解。

相关实践学习
基于Redis实现在线游戏积分排行榜
本场景将介绍如何基于Redis数据库实现在线游戏中的游戏玩家积分排行榜功能。
云数据库 Redis 版使用教程
云数据库Redis版是兼容Redis协议标准的、提供持久化的内存数据库服务,基于高可靠双机热备架构及可无缝扩展的集群架构,满足高读写性能场景及容量需弹性变配的业务需求。 产品详情:https://www.aliyun.com/product/kvstore     ------------------------------------------------------------------------- 阿里云数据库体验:数据库上云实战 开发者云会免费提供一台带自建MySQL的源数据库 ECS 实例和一台目标数据库 RDS实例。跟着指引,您可以一步步实现将ECS自建数据库迁移到目标数据库RDS。 点击下方链接,领取免费ECS&RDS资源,30分钟完成数据库上云实战!https://developer.aliyun.com/adc/scenario/51eefbd1894e42f6bb9acacadd3f9121?spm=a2c6h.13788135.J_3257954370.9.4ba85f24utseFl
相关文章
|
10月前
|
NoSQL Unix Linux
Redis核心技术与实践 03 | 高性能IO模型:为什么单线程Redis能那么快?
Redis核心技术与实践 03 | 高性能IO模型:为什么单线程Redis能那么快?
|
2月前
|
存储 消息中间件 缓存
Redis的高性能使得它非常适合用于实时分析场景
【5月更文挑战第15天】Redis在Python Web开发中扮演关键角色,常用于缓存系统,提高数据读取速度;会话管理,存储用户信息;分布式锁,确保数据一致性;排行榜和计数,利用有序集合和哈希结构;消息队列,基于列表结构实现异步处理;实时分析,高效处理实时数据。其丰富的数据结构和高性能使其在多种场景下应用广泛。
306 3
|
12天前
|
存储 缓存 NoSQL
Redis是一种高性能的内存数据库,常用于高并发环境下的缓存解决方案
【6月更文挑战第18天】**Redis摘要:** 高性能内存数据库,擅长高并发缓存。数据存内存,访问迅速;支持字符串、列表等多元数据类型;具备持久化防止数据丢失;丰富命令集便于操作;通过节点集群实现数据分片与负载均衡,增强可用性和扩展性。理想的缓存解决方案。
26 1
|
8月前
|
存储 消息中间件 NoSQL
深入了解Redis:高性能的内存数据库
深入了解Redis:高性能的内存数据库
|
23天前
|
存储 运维 NoSQL
Redis 分区:构建高性能、高可用的大规模数据存储解决方案
Redis 分区:构建高性能、高可用的大规模数据存储解决方案
|
5天前
|
NoSQL Redis
Redis的单线程和高性能
Redis 的单线程主要是指 Redis 的网络 I0 和键值对读写是由一个线程来完成的,这也是 Redis 对外提供键值存储服务的主要流程。 但Redis 的其他功能,比如持久化、异步删除、集群数据同步等,其实是由额外的线程执行的。
8 0
|
2月前
|
存储 缓存 监控
利用Redis构建高性能的缓存系统
在现代Web应用中,性能优化是提升用户体验和响应速度的关键。Redis作为一款开源的内存数据结构存储系统,因其出色的性能、丰富的数据结构和灵活的使用方式,成为了构建高性能缓存系统的首选工具。本文将探讨Redis在缓存系统中的应用,分析其优势,并通过实例展示如何结合Redis构建高效、可靠的缓存系统,以应对高并发、大数据量等挑战。
|
2月前
|
存储 缓存 监控
利用Redis构建高性能的缓存系统
在现今高负载、高并发的互联网应用中,缓存系统的重要性不言而喻。Redis,作为一款开源的、内存中的数据结构存储系统,它可以用作数据库、缓存和消息代理。本文将深入探讨Redis的核心特性,以及如何利用Redis构建高性能的缓存系统,并通过实际案例展示Redis在提升系统性能方面的巨大潜力。
|
2月前
|
存储 缓存 NoSQL
Redis 服务器指南:高性能内存数据库的完整使用指南
Redis 服务器指南:高性能内存数据库的完整使用指南
|
11月前
|
存储 消息中间件 缓存
Redis:高性能、多功能的内存数据库
Redis是一种开源、高性能的内存数据库,广泛应用于缓存、会话存储、实时数据处理等场景。本文将介绍Redis的特点、优势和主要功能,探讨它在不同应用领域中的应用场景,以及如何充分利用Redis提升应用性能和可靠性。无论是小型应用还是大规模的分布式系统,Redis都是一个值得关注的强大工具。
77 0