原文:https://scalegrid.io/blog/introduction-to-redis-data-structure-bitmaps/
位图(也称为位数组,位向量等)是紧凑存储位的数组数据结构。它可以用来实现一个简单的集数据结构。位数组可以有效地利用硬件中的位级并行性来快速执行操作。
Redis是一种内存数据结构服务器,它为位操作操作提供支持。但是,Redis中的位图没有特殊的数据结构。相反,基本的Redis结构支持位级操作:字符串。现在,Redis字符串的最大长度为512 MB。因此,Redis可以映射为位图的最大域是2^32(512 MB = 2^29字节= 2^32位)。
Redis中的位相关操作有两种:恒定时间(O(1)),例如,获取/设置特定位的操作和基本上对一组位操作的O(N)操作。在这些情况下,N是操作需要处理的位长度。我们来看一些例子。
命令
#SETBIT键偏移值:将位值'value'存储在'key'的偏移'offset'处。O(1) #返回存储在该偏移处的原始位值。 127.0.0.1:6379> setbit k 10 1 (整数)0 #GETBIT键偏移:在'key'中获取'offset'的值。O(1) 127.0.0.1:6379> getbit k 10 (整数)1 127.0.0.1:6379> getbit k 11 (整数)0 127.0.0.1:6379> getbit k 0 (整数)0 127.0.0.1:6379> setbit k 9 1 (整数)0 127.0.0.1:6379> setbit k 8 1 (整数)0 #并且因为它仍然是一个通用字符串,这里是一个get。 127.0.0.1:6379>得到k “\ X00 \ xe0” #“\ x00 \ xe0” - >“0000 0000 111” #BITCOUNT key [start end]:范围内的设置位数。上) #重要提示:start和end是字节而不是位 127.0.0.1:6379> bitcount k (整数)3 127.0.0.1:6379>设置m“喵” 好 #meow - > 01101101 01100101 01101111 01110111 127.0.0.1:6379> bitcount m (整数)21 #BITPOS键位[start] [end]:键范围内1或0的第1个位置。上) 127.0.0.1:6379>设置mykey“\ xff \ xf0 \ x00” 好 127.0.0.1:6379> BITPOS mykey 0 (整数)12
除了操作key本身的运算符之外,BITOP运算符还用于在多个key之间进行逐位逻辑运算。
#BITOP操作destkey键[key ...]。上) #操作可以是AND,OR,XOR和NOT 127.0.0.1:6379>设置“\ xff \ xff” 好 127.0.0.1:6379> bitop not nota a (整数)2 127.0.0.1:6379>得到nota “\ X00 \ X00” 127.0.0.1:6379>设置b“\ x0f \ x0f” 好 127.0.0.1:6379>设置c“\ xf0 \ xf0” 好 127.0.0.1:6379> BITOP OR orbc bc (整数)2 127.0.0.1:6379>获取orbc “\ XFF \ XFF” 127.0.0.1:6379> BITOP和andbc bc (整数)2 127.0.0.1:6379> get andbc “\ X00 \ X00” 127.0.0.1:6379> BITOP XOR xorbc bc (整数)2 127.0.0.1:6379>获取xorbc “\ XFF \ XFF”
内幕
由于位图操作没有自己的数据结构,因此没有特定的数据结构可供描述。Redis字符串本身实现为二进制安全字符串。Redis字符串数据结构在内部称为简单动态字符串(SDS)。它本质上是一个原生的char [],带有一些额外的簿记信息。具体的实现细节可以在这里找到。
位图函数的实现位于文件bitops.c中。
PS:鉴于位操作算法对关键操作系统和图形功能的重要性,大多数架构都为此类操作提供了特殊指令。阅读各种有趣的计算机算术算法的好地方是经典的Hacker's Delight。
应用
这个流行的GetSpool博客是使用位图在大型数据集上进行实时分析的一个很好的例子。它也是位图的经典用例的一个例子:将极大域的布尔信息存储到(相对)小空间中,同时保持良好的性能。
大小通常是真正大型位图的关注点,因为大多数有用的操作都是O(N)。为了避免使用大的key,Redis文档建议将大的key分成多个较小的key。在关键变大之前,BITCOUNT表现仍然可以接受。此时,建议分割键或使用范围参数逐步查询。处理慢BITOP操作的建议是在slave上运行它。因此,一般来说,处理中等大小的key,并通过将key分成多个key来规划未来的潜在增长是有意义的。
bitmap VS Sets
Sets提供的功能性质和bitmap操作类似。因此,这两种方法中哪一种更好会让人感到困惑。取决于实际情况。显然,此讨论仅对sets和bitmap可以实现的操作类型有效。
Sets通常效率很高,并且可以很好地扩展,通常是首选的数据结构,除非数据大到无法支持。Sets也更易于管理,程序和调试适用于大多数应用程序。不应低估Sets的易用性:操作bitmap的代码通常难以阅读,理解,调试和维护。即使值得范围非常大,Sets仍可能是合适的。例如,如果应用程序旨在跟踪对流行的电子商务站点的每日访问,则结果可能仍然适合于Sets,因为通常仅5-10%的整个用户群将每天访问该站点。对于预计每天登录60%的用户群的网站来说,这显然会发生变化。然后,鉴于在大量key上的逻辑位操作的大小和性能,位图变得更相关。Redis sets还具有不必将ID映射到位偏移的独特优势。同样,如果您的值大于2^32,那么Redis的sets 比位图划分域更容易使用。
针对MOOC的分析
下边是可以应用Redis位图操作的示例。比如说,您正在运行一个非常受欢迎的在线MOOC,数十万学生已经注册了该MOOC。促进课程的学术团队需要一个仪表板,他们可以看到学生进度的实时状态以及注册学生的一般背景。您决定通过Redis位图操作实现此功能。这是一步一步的方法。
- 创建计划以在学生ID和位图偏移之间进行映射。它可能就像ID是位图中的偏移一样简单。
- 课程开始后,创建并填充各种与人口统计相关的位图。例如,从同一所大学,教育水平,性别等入读其他MOOC的学生。
- 现在,随着课程的进展,您可以创建新的位图来记录课程进度。例如,完成了第1周所有讲座的学生,完成第1周所有作业的学生。
- 现在,基于这些键创建实时分析将是一个非常简单的练习,可以在拖放UI上完成。例如
- 一位教授想看看有多少学生在第1周(A)观看讲座但没有完成第1周(B)的作业:操作员:BITOP。操作:A AND(非B)。
- 完成第1周(A),第2周(B),第3周(C)和第4周(D)的所有作业的学生:操作员:BITOP。操作A和B和C和D.说,这些是通过该课程的人。
- 所有通过该课程的男学生(M)(如上所述,比如P):操作员:BITOP。操作:M和P.
- 通过该课程的学生人数:BITCOUNT P.
类似地,可以将任意数量的有趣群组设置为位图,并在其上运行这样的排列。