在移动应用的业务场景中,我们需要保存这样的信息:一个key关联一个数据集。常见的场景如下:给一个userId,判断用户的登录状态;显示用户在某月的签到次数和首次签到时间;近7天2亿用户签到情况,统计7天内连续登录用户总数;通常,我们面临的用户数量和访问量都是巨大的,比如百万级、千万级的用户量,或者是千万级甚至亿级的访问信息量。因此,我们必须选择一种能够非常高效地统计大量数据(例如数十亿)的集合类型。如何选择合适的数据集,首先要了解常用的统计模型,并使用合理的数据类型来解决实际问题。四种统计类型:二进制状态统计;汇总统计;排序统计;基数统计。本文将使用二进制状态统计类型作为实战系列的开篇,本文将使用除String、Set、Zset、List、hash之外的扩展数据类型Bitmap。文中涉及的指令可以通过在线Redis客户端运行调试,地址:https://try.redis.io/,超级方便。Message多分享多付出,前期为他人创造更多价值,不在乎回报。从长远来看,这些努力会给你带来成倍的回报。尤其是刚开始和别人合作的时候,不要担心短期的回报,没有多大意义,更多的是锻炼自己的眼光、观点和解决问题的能力。二进制状态统计代码兄弟,什么是二进制状态统计?即集合中元素的值只有0和1。在签到签到以及用户是否登录的场景中,只需要记录sign-in(1)或未登录(0)、已登录(1)或未登录(0)。如果我们在判断用户是否登录的场景中使用Redis的String类型(key->userId,value->0表示离线,1-登录),如果我们存储100万用户的登录状态,如果string的存储形式,需要存储100万个字符串,内存开销太大。代码兄,为什么String类型的内存开销大呢?String类型除了记录实际数据外,还需要额外的内存来记录数据长度、空间占用等信息。当保存的数据中包含字符串时,使用简单的动态字符串(SDS)结构保存String类型,如下图:SDSlen:占用4个字节,表示buf使用的长度。alloc:4字节,表示buf实际分配的长度,通常>len。buf:字节数组,保存实际数据,Redis自动在数组末尾添加一个“\0”,额外占用一个字节的开销。因此,在SDS中,除了buf中保存实际数据外,len和alloc都是额外的开销。另外还有RedisObject结构的开销,因为Redis有很多数据类型,不同的数据类型有相同的元数据来记录(比如上次访问的时间,引用次数等)。因此,Redis会使用一个RedisObject结构来统一记录这些元数据,同时指向实际的数据。对于二进制状态的场景,我们可以使用Bitmap来实现。比如我们用一个bit来表示登录状态,1亿个用户只占用1亿个bit的内存≈(100000000/8/1024/1024)12MB。空间占用的大概计算公式为:($offset/8/1024/1024)MB什么是Bitmap?Bitmap的底层数据结构使用String类型的SDS数据结构来存储位数组,而Redis存储每个字节数组的8个位被利用,每个位代表一个元素的二进制状态(0或1)。Bitmap可以看作是一个位单元数组,数组的每个单元只能存储0或1,数组的下标在Bitmap中称为偏移量。为了直观显示,我们可以理解为buf数组的每个字节用一行来表示,每一行有8位,8个格子代表这个字节中的8位,如下图所示:Bitmap8bitsFormaByte,所以Bitmap会大大节省存储空间。这就是Bitmap的优势。判断用户登录状态如何在大量用户中使用Bitmap判断用户是否在线?Bitmap提供了GETBIT和SETBIT操作,通过一个偏移值offset来读写bit数组的offset位置的bit,需要注意的是offset是从0开始的。只需要一个key=login_status来存储用户登录状态集合数据,以用户ID为偏移量,在线时置1,离线时置0。使用GETBIT判断对应用户是否在线。5亿用户只需要6MB的空间。SETBIT命令SETBIT
