接上一篇文章的内容
除了基础的string,list,map,set,zset5种数据结构之外,还有两种特殊的数据结构,分别是Bitmaps,HyperLogLog和GEO。由于string,list,map,set这几类数据结构在正常的工作中经常会接触到,我们主要针对zset,GEO,Bitmaps,HyperLogLog4种数据结构进行分析,并尝试考虑一下这三类数据结构的应用场景。
Bitmaps
- Bitmaps不是一种数据结构,是只有二进制数字的字符串,可以理解为二进制位的数组,比如:“0101001” ,可以理解为[0,1,0,1,0,0,1], 数组下标在这里称为偏移量(可以理解为内存的那个偏移量)从0开始递增;
- Bitmaps最大的优点是内存占用小,只要业务设计上是合理的,可以充分利用每一位;
- Bitmaps的位操作:
2.1 特定bit位的操作,比如设置/获取某个特定比特位的值,setbit key1 10 1 ,意思就是设置key1对应的Bitmaps的第10位为1; 0010000000经过setbit key1 10 1后就变成了0010000001。同理getbit key1 10;
2.2 bitcount key start end: 获取范围内,bitmaps有多少位为1的位数量;
2.3 bitop 位运算,bitop and|or|not|xor key_result [key1,key2,key3…], 比如bittop and keyres key1 key2,
keyres 就是key1和key2进行位运算and后的结果;
2.4 bitpos key 1 计算出bitmaps中第一个值为1的偏移量 - 单个bitmaps的最大长度是512MB,即2的32次方个比特位,简单来说就是字符串的最大长度可以到4294967296。
Bitmaps应用场景
基本上redis的书籍以及网上的博客在分析Bitmaps的应用场景的时候都会提到活跃用户,用户签到等应用场景。我们来分析一下为什么Bitmaps的数据结构这么适合用户场景:
我们先假设不用Bitmaps的情况,是如何把用户签到信息保存在redis中的。在redis中只记录活跃用户的id,那肯定要选择集合set(链表list也可以)。假如我需要保存的用户的id分别为1,2,3,4,5,6,7,set中会保存为[1,2,3,4,5,6,7],而在Bitmaps我们需要保存为"1111111"。这么一看好像也没有节省空间,事实上每个int类型的数字都会占用32bit或者64bit的空间,而对于Bitmaps来说每一个数字对应的还是1位。在这种情况下Bitmaps能节省很多内存空间。
当然Bitmaps在每日用户访问量大的情况下会节省空间资源,同时,如果每天活跃用户量较少的网站,Bitmaps体现他的劣势了,set只要存储少量的活跃用户,内存空间占用减少,但是对于Bitmaps来说,可能还是需要占用相同的内存,比如用户id是40000,在set中只要保存一个64位数字,但是在Bitmaps中就需要至少一个40000位长的字符串。所以我们总结的用户应用场景使用Bitmaps的前提条件是每天有大量的活跃用户的情况下,使用Bitmaps可以节省内存空间。而且随着时间的推移,效果会越来越可观,因为这种数据存储都是按天存储的。
然后我们来考虑下活跃用户场景中,Bitmaps的一些功能点到底能实现哪些用户场景的功能点:
- 存储每日活跃用户ID,前提是用户id是数字自增长,uuid就没办法。第N位的值就代表id为N的用户当天是否签到,1代表签到,0代表未签到。例:set useractive:20181205 N-1 1;
- 统计指定日期活跃用户数量,比如要计算20181205这一天活跃用户的数量,bitcount useractive:20181205 ;
- 获取连续几天都签到成功的用户的列表,比如要获得20181204到20181206三天内,每天都签到的用户的列表,bitop and keyresult useractive:20181204 useractive:20181205 useractive:20181206 ;
- 同理bitop or 可以获取任何签到过某一天的用户的列表
- 然后根据2,3,4我们还可以进一步统计比如一个月全勤用户的id列表以及数量等等功能应用
具体使用场景:小说的每一页是否已阅,人的性别记录等等。
我们可以从数据库的位图索引来总结一些经验:适合于数量庞大的,id为数字的数据的布尔属性或者 有且仅有两种可能性值的属性的存储。
HyperLogLog
- HyperLogLog也不是一种新的数据结构,也是字符串结构…不知道存的是啥玩意,应该也是二进制的字符串(不确定)。每个HyperLogLog最大只需要12kb内存,就能计算接近2^64个不同的数据;
- HyperLogLog是对存入的数据进行基数计数:常用来统计一个集合中不重复的元素个数。
- HyperLogLog操作命令:
3.1 pfadd key keyall v1 v2 v3,向keyall中添加3个元素
3.2 pfcount keyall, 统计keyall的不重复的元素总数,3.1中是3个
3.3 pfmerge destkey sourcekey1 sourcekey2 求多个HyperLogLog的并集并赋值给destkey - HyperLogLog不存储数据,所以即使pfadd大量数据,也能节省内存空间。概率算法方面,我觉得自己都看不太懂,还是看人家专业的博客:https://blog.csdn.net/firenet1/article/details/77247649
HyperLogLog应用场景
我们就不用拿集合来做比较了,很明显相对于计算数据越多,内存消耗越大的集合,HyperLogLog最大的内存控制在12kb以内极大的节省了内存空间。但是HyperLogLog也存在一些问题:
- 只能计算基数(不重复的元素个数),不能获取数据本身;
- 存在一定的错误率,可能比实际的基数或多或少,0.81%相似值;
我们考虑下这种特性有哪些场景比较适合,容许小误差,节省空间。比如ip访问数量(UV),关键词检索数量。
Ip访问数量举例:
UV百度百科:UV是unique visitor的简写,是指通过互联网访问、浏览这个网页的自然人。
独立IP:是指独立用户/独立访客。指访问某个站点或点击某条新闻的不同IP地址的人数,独立IP只记录第一次进入网站的具有独立IP的访问者,假如一台电脑关机了,30分钟后重启,再次访问这个站那就再计算一次ip,在同一天内再次访问该网站则不计数。
那么使用HyperLogLog就可以计算一天时间单位内独立IP的访问数量。
具体使用场景:
2.1 统计注册IP数
2.2 统计每日访问IP数
2.3 页面UV数
2.4 统计在线用户
2.5 关键词检索统计
2.6 统计点赞数评价数的计数器功能
bitmap和hyperloglog配合使用,比如说bitmap标识哪些用户活跃,hyperloglog在线用户数
写不动了,下一篇继续写zset的结构,应用场景,以及其他几个数据结构的一些场景吧。