Redis位图与 HyperLogLog

Redis高级数据结构:位图与 HyperLogLog

个人博客

一、位图(bitmap)

1、什么是位图?

位图并不是什么特殊的数据结构,而是定义在String类型上的一个面向字节操作的集合,也就是byte数组,位图的最小单位是bit,每个bit的取值只能是0或者1。

2、位图的基本用法

  • 位图数据结构是可以自扩展的,如果设置了某个偏移位置超出了现有的内容范围,就会自动将位数组进行零扩充。

零存整取

首先我们先用python获取‘hello’的ASCII码的二进制值

>>> bin(ord('h'))
'0b1101000'
>>> bin(ord('e'))
'0b1100101'
>>> bin(ord('l'))
'0b1101100'
>>> bin(ord('o'))
'0b1101111'

由于位数组的顺序和字符的位顺序是相反的
所以hello所对应二进制位表示为为
01101000 01100101 01101100 01101100 01101111
12345678 9…
我们只需要在为1的地方设置为1即可

127.0.0.1:6379> setbit s 1 1
(integer) 0
127.0.0.1:6379> setbit s 2 1
(integer) 0
127.0.0.1:6379> setbit s 4 1
(integer) 0
127.0.0.1:6379> setbit s 9 1
(integer) 0
127.0.0.1:6379> setbit s 10 1
(integer) 0
127.0.0.1:6379> setbit s 13 1
(integer) 0
127.0.0.1:6379> setbit s 15 1
(integer) 0
127.0.0.1:6379> get s
"he"

零存零取

  • 使用单个位操作设置位值,使用单个位操作获取具体位值
127.0.0.1:6379> setbit w 1 1
(integer) 0
127.0.0.1:6379> setbit w 4 1
(integer) 0
127.0.0.1:6379> getbit w 1
(integer) 1
127.0.0.1:6379> getbit w 2
(integer) 0

整存零取

  • 使用字符串操作批量设置位值 , 使用单个位操作获取具体位值
127.0.0.1:6379> set w h
OK
127.0.0.1:6379> getbit w 1
(integer) 1
127.0.0.1:6379> getbit w 2
(integer) 1
127.0.0.1:6379> getbit w 3
(integer) 0
127.0.0.1:6379> getbit w 4
(integer) 1

如果对应位的字节是不可打印字符,客户端会显示该字符的十六进制形式

127.0.0.1:6379> setbit x 0 1
(integer) 0
127.0.0.1:6379> setbit x 1 1
(integer) 0
127.0.0.1:6379> get x
"\xc0"

3、位图的统计和查找

  • bitcount key [start end]统计指令,统计指定位置范围内1的个数 , 可用来统计用户一共可签到多少天。
  • bitpos key [start end]查找指令,查找指定范围内出现的第一个0 或者 1 , 可用来查找用户从哪一天开始签到的。
  • 但是start和end参数是字节索引,也就是说指定的范围必须是8的倍数,而不能任意指定。
127.0.0.1:6379> set w hello
OK
127.0.0.1:6379> bitcount w
(integer) 21
127.0.0.1:6379> bitcount w 0 0 #第一个字符中1 的位数
(integer) 3
127.0.0.1:6379> bitcount w 0 1
(integer) 7
127.0.0.1:6379> bitpos w 0 #第一个0 位
(integer) 0
127.0.0.1:6379> bitpos w 1
(integer) 1
127.0.0.1:6379> bitpos w 1 1 1 #从第二个字符算起,第一个1位
(integer) 9
127.0.0.1:6379> bitpos w 1 2 2
(integer) 17

4、位图的bitfield指令

  • setbit与getbit 指定位的值都是单个位的,如果要操作多个位就需要用到bitfield指令了
  • bitfield指令有三个子指令,分别为 ger 、 set 、 incrby,都可以对指定位片段进行读写,但是最多只能处理64个连续的位,如果超出,就得使用多个子指令。
  • bitfield 的incrby子指令,可能会产生溢出,bitfield对此有自己的溢出策略overflow子指令,默认为wrap(折返)、fail(失败)——报错不执行 、sat(截断)——如果溢出就停留在最大值或者最小值。overflow指令只会影响接下来的第一条指令。

不知道为什么我的客户端,在敲这条命令的时候报错,所以代码就先不贴了,待以后再试试

127.0.0.1:6379> set w hello
OK
127.0.0.1:6379> bitfield w get u4 0
(error) ERR unknown command 'bitfield'
127.0.0.1:6379> bitfield w get u4 0

二、HyperLogLog

Set的高级数据结构,HyperLogLog,它提供了不精确的去重计数方案,标准误差是0.81%。
应用场景:某个网页的每天的UV数据

1、HyperLogLog 使用方法

  • pfadd 增加计数 ,使用方法同set的sadd
  • pfcount 获取计数 , 使用方法同set的scard,直接获取计数值
  • pfmerge 用于将多个pf计数值合并起来形成一个新的pf值,应用:多个页面数据进行合并时使用
127.0.0.1:6379> pfadd codehole user1
(integer) 1
127.0.0.1:6379> pfcount codehole
(integer) 1
127.0.0.1:6379> pfadd codehole user2
(integer) 1
127.0.0.1:6379> pfadd codehole user3
(integer) 1
127.0.0.1:6379> pfadd codehole user4
(integer) 1
127.0.0.1:6379> pfadd codehole user5
(integer) 1
127.0.0.1:6379> pfcount codehole
(integer) 5

接下来增大数据量

public class PfTest {
    public static void main(String[] args) {
        Jedis jedis = new Jedis("127.0.0.1");
        for (int i = 0; i < 100000; i++){
            jedis.pfadd("codehole","user"+i);
        }
        long total = jedis.pfcount("codehole");
        System.out.println(total);
        jedis.close();
    }
}

---------
99715

Process finished with exit code 0

将上述程序执行俩次,会发现俩次的结果一样,这说明pf确实有去重功能,而且误差率也不是很高。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值