Redis实现唯一计数的3种方法分享,Redis在大数据中的应用

唯一计数是网站系统中十分常见的一个功能特性,例如网站需要统计每天访问的人数
unique visitor (也就是
UV)。计数问题很常见,但解决起来可能十分复杂:一是需要计数的量可能很大,比如大型的站点每天有数百万的人访问,数据量相当大;二是通常还希望扩展计数的维度,比如除了需要每天的
UV,还想知道每周或每月的 UV,这样导致计算十分复杂。

 导读

在关系数据库存储的系统里,实现唯一计数的方法就是 select count(distinct
<item_id>),它十分简单,但是如果数据量很大,这个语句执行是很慢的。用关系数据库另外一个问题是插入数据性能也不高。

redis是一个基于内存的key-value数据库,相对关系型数据库支持的数据结构更丰富,而且操作封装的非常简单易用。redis也支持主从、分布式、数据持久化等特性。

Redis
解决这类计数问题得心应手,相比关系数据库速度更快,消耗资源更少,甚至提供了
3 种不同的方法。

 
 redis在业务系统中经常用作缓存系统,即把热点数据或高频数据存到Redis,降低底层数据库负载、提高应用吞吐率;redis灵活的数据结构也能解决特定大数据统计中的痛点,这里结合实际项目,举些典型应用场景,以及个人的理解,当然也是蜻蜓点水,泛泛而谈,希望大方向上能对大家有所帮助,当然有不正之处也请不吝赐教。

1.基于 set

Sets

Redis 的 set
用于保存唯一的数据集合,通过它可以快速判断某一个元素是否存在于集合中,也可以快速计算某一个集合的元素个数,另外和可以合并集合到一个新的集合中。涉及的命令如下:

集合的最主要特征是成员唯一性,最常用场景是一些涉及到去重的计算,比如UV计算(去重用户数):每次当用户活跃时简单的调用SADD命令向集合添加UID,然后实时的用SCARD获取元素个数。还支持多个集合之间的交集(SINTER)、差集(SDIFF)和并集(
SUNION)运算,交集操作可以很便捷的计算留存率:(今日集合∩昨日集合)/昨日集合。集合是通过哈希表实现的,所以添加、删除、查找的复杂度都是O(1),即复杂度和元素个数无关。

复制代码 代码如下:

Sorted sets

SISMEMBER key member  # 判断 member 是否存在
SADD key member  # 往集合中加入 member
SCARD key   # 获取集合元素个数

    有序集合相比集合每个成员多了分数属性,成员会按照分数自动从小到大排序,当然和集合类似,成员也是唯一不重复的。常用查询方法:排名在指定范围的成员、分数在指定范围内的成员。应用场景:

基于 set
的方法简单有效,计数精确,适用面广,易于理解,它的缺点是消耗资源比较大(当然比起关系数据库是少很多的),如果元素个数很大(比如上亿的计数),消耗内存很恐怖。

1、TOPN排名,商品浏览量作为排名指标,实时查询TOPN的商品列表,其实现过程是:商品每次被访问时,用ZINCRBY增加商品浏览量,实时用ZREVRANGE key
0 N-1返回集合中TOPN的商品。

2.基于 bit

2、30分钟在线用户数,APP、WEB都有统计最近在线用户数的需求,是衡量产品当前活跃度的重要指标,有序集合也可以很容易实现:以登录时间戳作为用户分数,每次登录时更新用户分数,“ZADD key
登录时间戳 UID”,然后用“ZCOUNT key 30分钟前时间戳
now时间戳”命令查询最近登录用户数

Redis 的 bit 可以用于实现比 set 内存高度压缩的计数,它通过一个 bit 1 或
0 来存储某个元素是否存在信息。例如网站唯一访客计数,可以把 user_id 作为
bit 的偏移量 offset,设置为 1 表示有访问,使用 1 MB的空间就可以存放 800
多万用户的一天访问计数情况。涉及的命令如下:

   
 有序集合是通过跳表和散列表两个数据结构实现的,添加、删除元素都会执行O(log(N))的操作(N是集合的元素个数)。有序集合的关键是在对分数这个属性的理解上,从合适的角度看问题,会达到事半功倍的效果。

复制代码 代码如下:

Bitmaps

SETBIT key offset value  # 设置位信息
GETBIT key offset        # 获取位信息
BITCOUNT key [start end] # 计数
BITOP operation destkey key [key …]  # 位图合并

Redis允许使用二进制的数据作为Key(binary keys)
和二进制的数据作为Value(binary
values),bitmap就是用二进制的数据作为value。bitmap不是一个新的数据类型,而是在String类型上进行的扩展,相关的命令有setbit、getbit、bitcount等。

基于 bit 的方法比起 set
空间消耗小得多,但是它要求元素能否简单映射为位偏移,适用面窄了不少,另外它消耗的空间取决于最大偏移量,和计数值无关,如果最大偏移量很大,消耗内存也相当可观。

SETBIT key offset value:设置key在第offset处的bit值(只能是0或1)

3.基于 HyperLogLog

GETBIT key
offset:对key所存储的值,获取指定offset位置的bit位(结果是0或1)

实现超大数据量精确的唯一计数都是比较困难的,但是如果只是近似的话,计算科学里有很多高效的算法,其中
HyperLogLog Counting 就是其中非常著名的算法,它可以仅仅使用 12
k左右的内存,实现上亿的唯一计数,而且误差控制在百分之一左右。涉及的命令如下:

BITCOUNT key:计算key所存储的值,被设置为1的bit位数量

复制代码 代码如下:

上面提到集合统计UV的例子,如果是大数据量的统计,会占用很大内存空间,如一个上亿用户量的网站,消耗的内存也很恐怖。bitmap也同样可以实现UV统计:当有用户活跃时,只需设置该用户所在bit位为1,而计算UV数就是统计所有bit位为1的数量。比如用户10086的用户活跃:SETBIT
key 10086 1,获取某天的DAU:GETBIT key。

PFADD key element [element …]  # 加入元素
PFCOUNT key [key …]   # 计数

bitmap处理大数据的排序、查询效率非常高而且能节省极大内存空间,最大1亿的偏移量大约占用12M内存,但也有几个显而易见缺点:1、元素是否能简单的映射为偏移量,就是待统计的元素是否能映射为Long类型 
2、消耗的空间取决于最大偏移量,和基数无关。

这种计数方法真的很神奇,我也没有彻底弄明白,有兴趣可以深入研究相关文章。

HyperLogLog

redis
提供的这三种唯一计数方式各有优劣,可以充分满足不同情况下的计数要求。

不像SET和Bitmaps是精确统计的,HyperLogLog(HLL)是一个概率数据结构,用于估计一个集合内的基数(元素个数),又叫基数统计。其统计是有误差的,可能会比实际稍微多一些或者稍微少一些,但会控制在合理的范围内,如果对误差是可接受的,HyperLogLog是一个最佳选择。假设是UV计算,每次用户活跃时,用PFADD添加元素到HLL,随时用PFCOUNT查看集合的基数,就可以实时统计UV。因为不会将元素真正添加到HLL,所以不能判断一个元素是否在HLL里存在。

您可能感兴趣的文章:

  • 超强、超详细Redis数据库入门教程
  • redis常用命令、常见错误、配置技巧等分享
  • Redis操作命令总结
  • Redis中5种数据结构的使用场景介绍
  • 让Redis在你的系统中发挥更大作用的几点建议
  • java遍历读取整个redis数据库实例
  • Redis数据库的使用场景介绍(避免误用Redis)
  • redis启动流程介绍
  • Redis数据库的应用场景介绍
  • Redis介绍和使用场景详解

Hashes

   
一个Hash类型有多个字段(属性),很像一个对象,但Hashes字段数量是没有限制的。一般是把一类数据放到一个key里,然后通过字段表示不同含义。

常用命令:HMGET:一次获取多个hash字段的值

HINCYBY:增加hash字段的值,用于计数

原子操作

redis单个命令都是原子操作,比如INCY,多客户端对同一个key进行INCY操作的情况下,不会发生客户端1读取key值1,客户端2同时读取key值1,然后客户端1和客户端2都对key进行加一操作,设置key的值为2;比如MSET,给多个key赋值,要么都设置成功,要么都设置失败,不会存在一部分key设置成功,一部分可以设置失败的情况

总结

在大数据分析架构里,一般是spark、storm作为计算框架,计算后的结果存到redis。要对redis的数据结构有清晰认识,理解各自优缺点、实用场景,redis价值才能最大化。

本文首发于公众号:data之道

图片 1

Post Author: admin

发表评论

电子邮件地址不会被公开。 必填项已用*标注