摘要:高斯Redis,计数的最佳选择!
本文分享自华为云社区《华为云PB级数据库GaussDB揭秘第八期:用高斯 Redis 进行计数》,原文作者:心机胖。
一、背景当我们打开手机刷微博时,就要开始和各种各样的计数器打交道了。我们注册一个帐号后,微博就会给我们记录一组数据:关注数、粉丝数、动态数…;我们刷帖时,关注每天的热搜情况,微博需要为每个热搜记录一组搜索量。在这一串数据后面,是一个个计数器在工作。
计数器可以分为常规计数器和基数计数器,对于常规计数器,只需要对计数器进行简单的增减即可;对于基数计数器,需要对元素进行去重,比如统计搜索量时,需要保证每个用户的多次搜索只统计一次。对于这两种需求,Redis 都有对应的数据类型进行统计。然而开源 Redis 是一个弱一致性的数据库,在特定的场景下,弱一致的计数不能满足业务需求,为此,我们需要一个强一致的数据库进行计数。
GaussDB(下文简称高斯Redis),是华为自研的强一致、持久化 NoSQL 数据库,兼容 Redis5.0 协议。本文将介绍常规计数器与基数计数器的应用场景及使用高斯 Redis 实现计数。
二、常规计数器2.1如何使用 Redis 进行常规计数Redis 实现常规计数器有两种数据类型适合:String 和 Hash。
2.1.1使用string 计数当我们需要维护的计数器数目较少,比如统计网站的注册用户数时,适合使用 String 类型的计数器。Redis 提供的 Incr 和 Decr 命令分别对 String 类型的 key 值进行增一与减一操作:
127.0.0.1:6379> SET counter 100OK127.0.0.1:6379> INCR counter 101127.0.0.1:6379> DECR counter 100
除Incr与Decr命令外,Redis String 类型还提供 Incrby 与 Decrby 命令,语法格式为:
- incrby: INCRBY key count
将 key 增加 count,count 可正可负,返回 key 的结果:
127.0.0.1:6379> INCRBY counter 10 10127.0.0.1:6379> INCRBY counter -20 -10
- decrby: DECRBY key count
将 key 减少 count,count 可正可负,返回 key 的结果:
127.0.0.1:6379> DECRBY counter 10 -10127.0.0.1:6379> DECRBY counter -20 10
需要维护多个密切关联的计数器时,可以使用Hash结构进行计数。比如,当我们注册一个微博账号时,微博会给每个用户记录一些用户数据,比如粉丝数、关注数等,这些数据都绑定到对应用户上,因此可以将这组计数器记录在同一个Hash key中,使用 hincrby 命令,语法格式为:
- hincrby: HINCRBY key filed count
将 Hash key 的 filed 增加 count,count 可正可负,返回对应 field 的结果:
127.0.0.1:6379> HGET userid field127.0.0.1:6379> HINCRBY userid field 1 1127.0.0.1:6379> HINCRBY userid field -1 0127.0.0.1:6379> HGET userid field"0"
常规计数器的使用场景很广泛,对于社交产品,用户的粉丝数、关注数,帖子的点赞数、收藏数…;对于视频网站,需要统计视频的播放次数(PV统计,Page View);对于电商秒杀,需要统计商品数量并进行流量控制。在并发量高的情况下,Redis 的性能优势明显,非常适合以上场景。
以电商秒杀业务为例,为了处理高并发读写,通常在MySQL上层部署Redis作为缓存。为了抗住大流量,使用计数器作限流。比如,当我们想控制每秒1万次请求时,可以初始化一个counter=10000,随后每次请求过来,都对counter减一,当counter 归零后,阻塞后续的请求。每隔一段时间,重置counter=10000,以此保证大流量不会冲击底层的MySQL。
三、基数统计:HyperLogLog 的原理及使用基数计数(cardinality counting)是指在一个数据集合中,统计不重复元素的个数,是实际应用中一种常见的场景。比如统计一段时间内访问某个网站的用户数,网络游戏的日活用户数量等。
在数据量较小情况下,我们可以把所有数据保存下来进行去重统计。Redis 中,可以使用 Set 与 Zset 将数据保存下来,然后统计集合中的元素数量。而当数据量较大时,该方法会消耗较大的存储空间,需要考虑其它的算法。
考虑一种情况,当我们登录微博时,微博会记录我们的登录情况,并统计每天有多少活跃用户。很显然,我们不需要也不应该记录活跃用户的ID,并且,少量误差对活跃用户数量的统计使用影响不大,这种场景下,我们可以使用 HyperLogLog 进行计数。HyperLogLog 是一种使用极少内存实现巨量统计的计数算法,非常适合大数据场景的基数估计,在 Redis 中被实现为一种数据类型。
3.1HyperLogLog 原理介绍3.1.1从伯努利试验到基数计数
HyperLogLog 是一种基数估计算法,其思想来自于伯努利过程。
简单来说,伯努利过程就是一个抛硬币的过程。抛一次硬币,结果为正面或者反面的概率都是1/2。记正面为1,反面为0,如果抛硬币多次,直到出现第一次正面时停止,记为一次投掷试验,并且得到一个投掷结果的序列,比如“001”,我们可以知道,这个序列出现的概率是 。
反过来,如果我们持续进行投掷试验,当出现第一次“001”序列时,我们可以简单估算出,我们投掷试验次数为8(事实上,这是一个极大似然估计)。
这里,我们有了一个简单的估计算法。我们只需要记录哈希结果中第一个“1”出现的位置的最大值即可,但很明显,当数据量较小时,这样一个估计值误差会很大,而且单个元素的对估计值的影响不平滑。
3.1.2分桶平均减小误差
为了减小单一估计量的影响,HyperLogLog 使用分桶多次试验的方法减小误差。方法是将哈希后的bitmap中前若干位当成桶的编号,剩余位当成试验结果。
3.2Redis 中的 HyperLogLogRedis 将HyperLogLog 实现成一种数据类型,对于每个元素,Redis将其Hash成64位的二进制串,用低14位用来表示bucket的下标(所以桶的个数为1<<14=16384),剩余的位用来模拟伯努利分布,每个桶需要6个bit;最多能够对 个元素进行统计,内存占用约12 k;其标准误差为 0.81%。
Redis 支持的 HyperLogLog 命令只有3个,pfadd,pfcoun,pfmerge, 其语法如下:
- pfadd:将所有元素参数添加到 HyperLogLog 数据结构中
语法:PFADD key element1 [element2…]
如果至少有一个元素被添加返回1,否则返回0
如果没有指定 element,则创建 hyperloglog key
127.0.0.1:6379> pfadd key1 ele1 ele2 1127.0.0.1:6379> pfadd key1 0127.0.0.1:6379> pfadd key2 0
- pfcount:返回给定的HyperLogLog的基数估计值
语法:PFCOUNT key1 [key2 … ]
返回对应 HyperLogLog 的基数值,多个key时,返回多个key的合并后的基数值。
127.0.0.1:6379> pfcount key1 0127.0.0.1:6379> pfadd key1 ele1 ele2 1127.0.0.1:6379> pfadd key2 ele1 ele3 1127.0.0.1:6379> pfcount key1 2127.0.0.1:6379> pfcount key1 key2 3
- pfmerge:将多个 HyperLogLog 合并为一个
语法:PFMERGE destkey sourcekey1 [sourcekey2 …]
将 sourcekey 与 destkey 合并,当 destkey 不存在时,会创建 destkey
返回OK
127.0.0.1:6379> pfadd key1 ele1 ele2 1127.0.0.1:6379> pfadd key2 ele1 ele3 1127.0.0.1:6379> pfcount key3 0127.0.0.1:6379> pfmerge key3 key1 key2OK127.0.0.1:6379> pfcount key3 3
HyperLogLog 作为一种计算大数据量的基数统计算法,在统计注册用户数,每日访问IP数,实时统计在线用户数等场景可以大显神威。
- 统计网站的UV
对于一个网页,我们想要知道这个网页的受关注程度,可以统计一下有多少用户(IP)点击了这个网页。为此,我们给每个时间段设置一条记录,比如,127.0.0.1这个IP在2021年1月1日1点的时候访问了网页:
pfadd key_prefix_2021010101 "127.0.0.1"
当需要统计这一天0-1点这一个小时一共有多少IP访问了这个网页时:
pfcount key_prefix_2021010101
需要统计上午8到12点的网页访问情况:
pfcount key_prefix_2021010109 …… key_prefix_2021010112
一天结束了,需要统计并保存这一天访问情况:
pfmerge key_prefix_2021010101 ...... key_prefix_2021010124
对于一个热门的网页,这样一个计数的方式显然能够极大的节约存储空间。
- 用户画像
用户画像是根据用户在互联网上留下的各种数据,给用户贴上一系列的标签,比如用户的性别,年龄,爱好等。在进行数据分析时,可以使用 HyperLogLog 进行数据的保存与分析。
4.2高斯 Redis 如何解决高斯 Redis 借助高斯品牌的“存算分离”架构,将全量数据下沉到强一致存储层(DFV Pool),彻底摒弃了开源 Redis 的异步复制机制;计算层将海量数据进行分片,在故障场景下,自动进行接管,实现了服务的高可用。
五、结语高斯 Redis 在社区版 Redis 的基础上,结合华为自研强一致存储DFV Pool,具有强一致、秒扩容、超可用、低成本等优势,保证了计数的准确性、可靠性。
本文作者:华为云高斯Redis团队。
杭州西安深圳简历投递:yuwenlong4@huawei.com
更多技术文章,关注高斯Redis官方博客:https://bbs.huaweicloud.com/community/usersnew/id_1614151726110813
六、参考资料1.《Redis应用场景-计数器》
https://blog.csdn.net/nklinsirui/article/details/106432298
2.《HyperLogLog 算法的原理讲解以及 Redis 是如何应用它的》
https://juejin.cn/post/6844903785744056333
3.《五种常用基数估计算法效果实验及实践建议》
http://blog.codinglabs.org/articles/cardinality-estimate-exper.html
4.《【云驻共创】从相识到相惜:Redis与计算存储分离四部曲》
https://bbs.huaweicloud.com/blogs/253041
5.《华为云PB级数据库GaussDB揭秘第七期:高斯Redis与强一致》
华为云PB级数据库GaussDB揭秘第七期:高斯Redis与强一致
点击关注,第一时间了解华为云新鲜技术~