• Redis系列:HyperLogLog实现海量数据基数统计


    1 前言

    我们来回顾下在这个系列的篇 深刻理解高性能Redis的本质 中介绍过Redis的几种基本数据结构,
    它服务于各种不同的业务场景而设计的,比如:

    • 动态字符串(REDIS_STRING):整数(REDIS_ENCODING_INT)、字符串(REDIS_ENCODING_RAW)
    • 双端列表(REDIS_ENCODING_LINKEDLIST)
    • 压缩列表(REDIS_ENCODING_ZIPLIST)
    • 跳跃表(REDIS_ENCODING_SKIPLIST)
    • 哈希表(REDIS_HASH)
    • 整数集合(REDIS_ENCODING_INTSET)

    除了这些常见数据类型,还有一些不常用的数据类型,如 BitMap、Geo、HyperLogLog 等等,他们在各自的方向为不同的类型的数据统计给出解决方案。

    • 位图(BitMap)计算:可以应用于任何大数据场景下的二值计算,比如 是否登录、是否在线、是否签到、用户性别状态、IP黑名单、是否VIP用户统计 等等场景。
    • Geo类型:记录地理空间信息,如 地理坐标存储、位置计算、距离计算等能力,普遍运用在地图业务中的各种场景。

    这一篇我们来介绍下HyperLogLog,HyperLogLog 主要用于Redis基数的统计,比如IP统计,用户访问量,页面访问量。

    2 关于HyperLogLog

    HyperLogLog 主要用于Redis 的基数统计,它的数据结构专门设计用来做数据合并和计算,并能节省大量的空间。
    基数计数( cardinality counting) 通常用来统计一个集合中不重复的元素个数 , 例如统计某个网站的UV、PV或者网站搜索的的关键词数量。
    在各种应用领域基数统计被广泛应用,如数据分析、网络监控指标、存储性能优化等。
    简单来说,基数计数就是记录集合中所有不重复的元素Su ,当新增元素Xa时,判断Su中是否包含,不包含则将其加入Su,包含则不加入,计数值就是Su 的元素数量总和。
    当然这种做法也存在两个问题:

    2.2 高效和海量特性

    如果我们使用普通集合,也能够实现对巨量数据的存储和统计么,但是存储量会大很多,性能也比较差。

    以百度搜索为例,如果要做百度指数的计算,针对来访IP进行统计。那么如果每天 有 1000 万 IP,一个 IP 占位 15 字节,那么 1000 万个 IP 就是 143M。

    1. 当统计的数据量变大时,相应的存储内存也会线性增长
    2. 当集合Su 变大,判断其是否包含新加入元素的成本变大

      2.1 实际应用场景

      很多计数类场景,比如 每日注册 IP 数、每日访问 IP 数、页面实时访问数 PV、访问用户数 UV等。
      因为主要的目标高效、巨量地进行计数,所以对存储的数据的内容并不关系。也就是说它只能用于统计数量,没办法知道具体的统计对象的内容。

    3. 统计单日一个页面的访问量(PV),单次访问就算一次。
    4. 统计单日一个页面的用户访问量(UV),即按照用户为维度计算,单个用户一天内多次访问也只算一次。
    5. 多个key的合并统计,某个门户网站的所有模块的PV聚合统计就是整个网站的总PV。
    10,000,000 * 15 /(1024 * 1024)  = 143.05 M
    

    如果使用 HyperLogLog ,那么在 Redis 中每个键占用的内容都是 12K,理论上能够存储 264 个值,即18446744073709551616,这个数是巨量,Java中long类型也只能计算到 262 。
    无论存储何值,它一个基于基数估算的算法HyperLogLog Counting(简称HLLC),使用少量固定的内存去存储并识别集合中的唯*一元素。
    HLLC采用了分桶平均的思想来消减误差,在Redis中, 有16384个桶 。而HyperLogLog的标准偏差公式是1.04 / sqrt(m),m 为桶的个数。所以

    1.04 / sqrt(16384) = 1.04 / 128 = 0.008125 
    

    所以这个计数的估算,是一个带有 0.81% 标准偏差的近似值。

    3 HyperLogLog所支持的能力

    HyperLogLog数据结构的命令有三个:PFADD、PFCOUNT、PFMERGE

    3.1 PFADD 添加计数

    Redis Pfadd 命令将所有元素添加到 HyperLogLog 数据结构中。

    语法如下:

    redis > PFADD key element [element ...]
    

    下面举例了网站统计模块添加IP的两种情况

    1. /* 对访问百度网站(key=baidu:ip_address)的IP进行添加 */
    2. redis> PFADD baidu:ip_address "192.168.0.1" "192.168.0.2" "192.168.0.3"
    3. (integer) 1
    4. /* 如果IP已经存在,则进行忽略,不对估计数量进行更新 */
    5. redis> PFADD baidu:ip_address "192.168.0.3"
    6. (integer) # IP已经存在

    3.2 PFCOUNT 统计数量

    Redis Pfcount 命令返回给定 HyperLogLog 的基数的估算值。
    语法如下:

    redis > PFCOUNT key [key ...]
    

    下面估算了访问IP的基数的值,返回 1034546 。

    1. redis> PFCOUNT baidu:ip_address
    2. (integer) 1034546

    3.3 PFMERGE 合并统计

    Redis PFMERGE 命令将多个 HyperLogLog 合并为一个 HyperLogLog ,合并后的 HyperLogLog 的基数估算值是对给定 HyperLogLog 进行并集计算得出的。
    所以有重复的会被统计成一条数据。
    合并得出的 HyperLogLog 会被储存在 destkey 键里面, 如果该键并不存在,那么命令在执行之前, 会先为该键创建一个空的 HyperLogLog 。
    语法如下:

    redis > PFMERGE destkey sourcekey [sourcekey ...]
    

    下面演示了合并和统计的过程:

    1. /* 统计百度 baidu:ip_address 访问IP */
    2. redis> PFADD baidu:ip_address "192.168.0.1" "192.168.0.2" "192.168.0.3"
    3. (integer) 1
    4. /* 统计淘宝 taobao:ip_address 访问IP */
    5. redis> PFADD taobao:ip_address "192.168.0.3" "192.168.0.4" "192.168.0.5"
    6. (integer) 1
    7. /* 合并且去重之后放在 total:ip_address */
    8. redis> PFMERGE total:ip_address baidu:ip_address taobao:ip_address
    9. OK
    10. /* 结果为5 */
    11. redis> PFCOUNT total:ip_address
    12. (integer) 5

    4 总结

    基数计数是用于统计一个集合中不重复的元素个数,好比平常需求场景有,统计页面的UV或者统计在线的用户数、注册IP数等。HyperLogLog 主要基于Redis能力下的基数统计。HyperLogLog的主要使用场景包括:

    1. 统计单日一个页面的访问量(PV),单次访问就算一次。
    2. 统计单日一个页面的用户访问量(UV),即按照用户为维度计算,单个用户一天内多次访问也只算一次。
    3. 多个key的合并统计,某个门户网站的所有模块的PV聚合统计就是整个网站的总PV。
  • 相关阅读:
    请问大家在什么网站上能查到英文文献?
    解决Oracle SQL语句性能问题——SQL语句改写(join、集合)
    Linux 基础操作手记四
    Python的pytest框架(6)--测试钩子(hooks)
    NumPy 中的排序方法(sort, argsort, lexsort, partition, argpartition, searchsorted)
    通过openwrt查看连接设备的IP,MAC地址,设备名
    深入解析 Socks5 代理与网络安全
    npm : 无法将“npm”项识别为 cmdlet、函数、脚本文件或可运行程序的名称。请检查名称的拼写,如果包括路径,请确保路径正确,然后再试一次。
    Linux------网络基础1
    小米汽车,能否在新能源汽车江湖站稳脚跟?
  • 原文地址:https://blog.csdn.net/Ly768768/article/details/138110340