11.Reids的特殊数据类型
Reids中,集合是一个常见的数据类型
而集合中经常面对一些常见操作
手机APP上的用户登录信息,一天对应一系列用户ID或者移动设备ID
电商网站上商品的评论列表
用户在手机APP上的签到打卡记录,一天对应多个用户的记录
应用网站上的网页访问信息,一个网站对应一系列的访问信息
对于这些常见的业务场景,通常会延伸会一些对集合中数据进行统计的操作
比如移动应用中,统计每天新增用户数和留存用户数
电商网站评论中,统计最新的评论
签到打卡中,统计一个月内连续打开的用户数
网页的访问记录中,需要统计独立访客数
这样,我们就需要对集合进行不同的操作,包含聚合统计,排序统计,二值状态统计,基数统计
我们按照上面的顺序,依次说明不同的统计方式及其Reids中的实现
1.聚合统计
比如常见的统计APP中每天的新增用户数和第二天留存用户数,
对于这个任务,我们可以先使用一个集合记录所有登录过APP的用户ID,然后使用另一个集合记录每天登陆的APP的用户ID,然后进行聚合
对于这种需要去重用户ID的操作,首选的数据结构就是Set
首先先使用一个集合统计所有登录过App的用户ID,直接使用Set类型,将key设置为user-login,记录的是用户ID,value是一个Set集合,保存了所有登录过App的用户Id
除此外,我们还需要保存每一天登录的用户ID,记录到一个新集合中,将这个集合叫做每日用户Set
key一般为: user-login-{date},value是Set集合,记录当前登录过的用户ID
统计每天新增的用户的时候,只需要计算每日用户Set和全用户Set的差集就可以了
比如如下的操作
SDIFFSTORE user-login-new user-login-20210804 user-login
获取到差集,这样user-loign-new就记录了新增的用户Id
然后再将新增的加入到user-login中
SUNIONSTORE user-login user-login user-login-20210804
计算留存也很简单了
只需要保存今天和前一天的交集就可以了
SINTERSTORE user-login-rem user-login-20210803 user-login-20210804
这些都是常见的交集差集的统计方式,但是由于Reids的单线程,可能会导致Reids实例阻塞
2.然后排序统计
比如查找电商网站上的最新评论列表
在Reids的常用的集合数据结构中 List 和 Sorted Set就是有序的集合
List是保证了元素进入的顺序,Sorted Set则可以根据权重进行排序,在元素中插入Sorted Set确定权重值,先插入的权重小,后插入的权重大
但是List有一些缺陷,比如在查询评论分页的时候,如果查询第二页,可以执行如下的命令
LRANGE producet 5,9
获取第二页的数据,但是在展示第二页的时候,产生了一个新的评论G,评论G就会被LPUSH命令插入到List的对头,这样第二页会将第一页原本不在的数据展示出来
这就是Sorted Set的优点,其实根据元素的实际权重来排序并获取数据的
我们可以按照时间的先后给每个评论设置一个权重值,然后保存到Sorted Set,并利用ZRANGEBYSCORE命令进行返回规定的数据
比如下面的操作
ZRANGEBYSCORE comments X Y
3.二值状态统计
有一种场景就是集合元素的取值就只有0和1两种,在签到记录中,记录签到就只有签到(1)或者未签到(0),这就是二值状态
统计签到的时候,一天的签到用一个bit位就表示,一年的签到也就365个bit位
对于这种bit的操作,Redis提供了Bitmap,利用String作为底层数据结构,将字节数组的每个bit利用起来,表示一个元素的二值状态,Bitmap就可以看做是一个bit数组
其提供了GETBIG 和 SETBIT操作,根据偏移量来对一个bit位进行读写,Bitmap的偏移量是从0开始的,这是操作中需要记住的,Bitmap还提供了BITCOUNT,来统计这个bit数组中所有 ”1″的个数
比如,我们先对一个用户进行记录
SETBIT user-sigin-3000-202108 2 1
表示id为3000的用户在8月3号签到了
BITCOUNT user-sign-3000-202108
这样就知道这个用户在8月份的签到记录了
而且Bitmap支持利用BITOP命令对多个Bitmap做 与 或 异或这类操作,并保存在一个Bitmap中
我们按照与的操作为例讲解一下
三个Bitmap bm1 bm2 bm3对应Bit位做与的操作,保存到一个新的Bitmap中
BITOP AND resmap bm1 bm2 bm3
在记录数据的二值状态的地方,使用Bitmap是一个非常高效的方式,记录海量数据时候能够有效的节省内存空间
3.基数统计
相对来说比较简单的一个统计方案,对应到之前说的场景,就是统计网页的UV
统计UV的时候,需要进行用户层面的去重,常见的默认支持去重的集合类型,就是Set
Set的去重功能就能保证不会记录重复的用户,而且支持SCARD去返回一个集合中的元素个数
但是如果页面成千上千万,每个页面都有一个Set,消耗的内存可能很大
或者使用Hash类型记录UV,将用户ID作为key,利用HSET将用户ID记录一个值
HSET page1:uv user1 1
但是也存在一个内存消耗大的问题
那么是否Redis提供了相对好用的数据类型?其中就是HyperLogLog了
HyperLogLog是一个统计基数的数据集合类型,当集合元素数量多的时候,计算基数所需的空间是固定的,而且很小
统计UV的时候,可以使用PFADD命令将新访问的用户添加到HyperLogLog中
PFADD page1:uv user1 user2 user3 user1
而且可以使用PFCOUNT命令来直接获取page1的UV值了,其作用就是返回HyperLogLog的统计结果
PFCOUNT page1:uv
但是HyperLogLog的统计是基于概率的,所以统计数据有一定的误差,但是误差不会很大
将不同的数据类型的支持总结在了下面
最后说一句
如果在统计用户签到的时候
将使用命令改为
SETBIT uid:sign:20200803 3000 1
是不是方便统计呢,有没有弊端?