Redis bitmaps

认真写文章,用心作分享。java

我的网站:yasinshaw.com面试

公众号:xy的技术圈算法

前面的文章介绍了Redis的五种最经常使用的对象及其底层的数据结构。这篇文章主要介绍一下一个不那么经常使用,却很是适用于一些特殊场景的对象:bitmaps。数据库

面临的问题

先考虑几个常见的场景:编程

  • 查询今天登录的用户数量
  • 查询今天有哪些用户登陆了
  • 查询某个用户是否点赞过某篇文章
  • 查询某个用户是否连续两天都登录
  • 查询点赞过文章A且点赞过文章B的用户

这些需求可使用数据库来实现,使用一些日志表,再经过SQL查询出来。但这样会浪费大量的磁盘空间,并且性能还很低。在数据量比较大的状况下,使用数据库来查询分析会很是慢。数组

这里要感谢咱们计算机的科学家大佬们,发明了一种数据结构能够用来解决这类问题。它就是BitMap。也就是这篇文章主要介绍的Redis bitmaps底层使用的数据结构。数据结构

《编程珠玑》中第一篇讲的就是使用BitMap来排序大文件里面的数据。在一些面试里也会有相似的题目。性能

面试题目:一个10G的文件,里面所有是天然数,一行一个,乱序排列,对其排序。在32位机器上面完成,内存限制为2G。网站

LeetCode上有一个算法题,也是可使用BitMap来解决:日志

算法题目:从0到n之间取出n个不一样的数,找出漏掉的那个。注意:你的算法应当具备线性的时间复杂度。你能实现只占用常数额外空间复杂度的算法吗?

BitMap原理

BitMap实际上是一个数据结构,它是利用了位运算的高性能,来存储和计算一些信息。接下来咱们来介绍一下BitMap的原理。

BitMap是基于bit位的位置来记录信息的。好比咱们如今有一个8位的BitMap。最开始时,全部位上都是0。

0, 0, 0, 0, 0, 0, 0, 0

而后有这么一个需求:假设今天有id为1,2,4,7这四个用户登陆了咱们的系统,咱们想要把这个登陆信息记录下来,就只须要在相应的位置标记为1就好了:

1, 1, 0, 1, 0, 0, 1, 0

这个时候,你想知道id为7的用户今天是否登陆过系统,就只须要去看这个BitMap里面第7位是否是1就能够了。

能够看到,若是你使用一个list或者set来存的话,若是一个id是一个byte,占8位(真实状况多是long类型,占64位),那8个id就要占64位,而使用BitMap只须要占用8位。同理,若是咱们的id字段占了64位,那就能够节省64倍的空间。并且,能够利用位运算的特性,来快速实现统计、并集、交集等操做。

看过文章A的人有:1, 2, 4, 7:

1, 1, 0, 1, 0, 0, 1, 0

看过文章B的人有:1, 3, 4, 8:

1, 0, 1, 1, 0, 0, 0, 1

看过文章A且看过文章B的人有:

1, 1, 0, 1, 0, 0, 1, 0

&

1, 0, 1, 1, 0, 0, 0, 1

=

1, 0, 0, 1, 0, 0, 0, 0

获得看过文章A且看过文章B的人有:1, 4

上面的例子只能放8位,若是个人id是9怎么办?

BitMap是一个一个上面这样的数据来组成的。你可使用一个能够扩容的整数数组来作,好比long数组。因此能够无限扩展。

若是id比较稀疏怎么办?

好比我要存入的id多是1,101,1001这种比较稀疏的,若是用BitMap就会浪费一些空间。虽然如今开源的一些实现能够经过记录偏移量来解决这个问题,但也会由于频繁的分裂影响性能。这种场景下,其实不建议使用BitMap

有兴趣的同窗能够了解一下谷歌开源的EWAHCompressedBitMap,它解决了输入稀疏的问题。

BitMap实现

由于笔者主要熟悉Java语言,因此介绍一下Java语言对BitMap的实现。

JDK提供了一个BitMap的实现,叫BitSet,位于java.util包下。其底层使用的是一个long类型的数组,一个long表明一个word。但BitSet没有解决上面提到的输入稀疏的问题。谷歌开源的EWAHCompressedBitMap解决了输入稀疏的问题。

Redis提供了bitmaps对象。实际上是使用的string对象,底层使用了咱们上篇文章提到的SDS。因此会有512M的最大限制,即最多能存2^32个数据。若是你的id是long类型的,占64位,那可使用两个bitmaps来存

由SDS的底层实现可知,它是能够扩容和缩容的,可是若是输入比较稀疏,仍然会浪费大量的内存。因此若是输入很稀疏,也不建议使用Redis的bitmaps。

Redis bitmaps基本操做

carbon 6.png

总结

Redis提供了bitmaps对象。它在某些场景下能够节省空间,并显著提高性能。但若是输入比较稀疏(好比网站注册用户有1亿,但天天只有10万用户登陆),那还不如使用set。

另一方面,输入只能是整形。若是是字符串类型的,就无法使用这个数据结构了。

关注公众号:xy的技术圈

相关文章
相关标签/搜索