Redis实战:巧妙利用Bitmap访问亿级海量数据统计
在移动应用业务场景中,我们需要保存这样的信息:为一个数据集合分配一个key。
常见场景包括:
- 输入userId,判断用户的登录状态;
- 显示当月登录次数及用户首次登录时间;
- 近7天内2亿用户登录情况,统计7天内连续登录的用户数量;
通常情况下,用户和访问量都很大,比如几百万、几千万的用户,或者几千万甚至几十亿的访问信息。
因此我们需要选择一种可以非常高效地统计大量数据(例如数十亿)的集合类型。
要选择正确的数据集,首先要了解常用的统计模型,并应用对数据的合理理解来解决实际问题。
四种统计类型:
- 二进制状态统计;
- 聚合统计;
- 订单统计;
- 基本统计数据。
本文使用二进制状态统计类型作为练习系列的开始。 Wen 中将 使用除 String、Set、Zset、List 和 hash 本文的说明可以通过在线Redis客户端(https://try.redis.io/)运行和调试,极其方便。 分享更多,付出更多。在不考虑投资回报率的情况下,尽早为他人创造更多价值。从长远来看,这些努力将会放大地回报给你。 尤其是当您第一次开始与他人合作时,不要担心短期回报。这没有多大意义。相反,它是关于锻炼你自己的愿景、观点和解决问题的能力。 马哥,什么是二进制状态统计? 即集合的元素的值只有0和1。在登录场景以及用户是否登录的情况下,只记录 如果我们使用Redis String类型实现来判断用户是否登录(key -> userId, value -> 0表示离线,1 - 登录),如果存储100万个用户如果将登录状态存储为字符串,则必须存储100万个字符串,这会消耗太多内存。 妈妈、兄弟,为什么String类型内存大呢?除了记录实际数据之外, 字符串类型还需要额外的内存来记录数据长度、空间使用情况等信息。 如果保存的数据包含字符串,则String类型以简单动态字符串(SDS)结构保存,如下图: 所以在SDS中,除了buf保存实际数据之外,len和alloc代表了额外的额外成本。 此外,还有一个额外的RedisObject结构的头,因为Redis中有很多数据类型,相同的元数据附加到不同的数据类型上(例如,上次访问时间、引用) 。号等)。 所以Redis使用RedisObject结构来统一捕获这些元数据并指向实际数据。 对于二进制状态的场景,我们可以使用 Bitmap 来访问它们。比如我们用一位来表示登录状态,一亿个用户只占用一亿位内存≈(100000000 / 8/1024/1024) 12 MB。 什么是位图? Bitmap底层数据结构采用String类型的SDS数据结构来保存位数组。 Redis 使用每个字节数组的 8 位。每个位表示一个元素的二进制状态(不是 0 就是 1)。 位图也可以想象为包含位单元的数组。数组的每个单元只能存储0或1。数组的下标称为位图偏移量。 为了直观直观,我们可以理解为buf数组的每个字节由一行表示,每行8位,8个格子分别代表这个字节中的8位,如图如下: 1 个字节创建 8 位,因此 Bitmap 节省了大量的存储空间。 这就是Bitmap的优点。 如何使用Bitmap判断大量用户中的某个用户是否在线? Bitmap 提供 只需要一个key=login_status即可存储用户登录状态的聚合数据。使用用户ID作为偏移量,在线时设置为1,离线时设置为0。使用 SETBIT 命令 设置或清除偏移处键值的位值(只能为 0 或 1)。 GETBIT 命令 获取键值偏移中的位的值。如果键不存在,则返回0。 假设我们要判断用户ID 10086的登录状态: 首先执行以下命令,表明该用户已登录。 第二步是验证用户是否已登录。统计中的返回值1记录用1位代表每个用户每天的登录情况,每年的登录情况只需要365位。一个月最多有31天,只需要31位。 比如2021年5月统计号为89757的用户如何登录?键 第一步是执行以下命令来记录用户在2021年5月16日的登录情况。 第二步,判断用户号89757是否在2021年5月16日登录。 ❀第三步,统计该用户5月份的登录次数,使用命令 这样我们就可以了解每个月用户的登录情况,是不是很棒? 本月首次登录时间如何计算? Redis 提供了 默认情况下,该命令检测整个位图。用户可以使用可选参数 因此,通过执行以下命令,我们得到2021年5月的userID = 89757 首次登录日期: 我们必须返回偏移值+1。从0开始。 连续7天记录了1亿用户的登录明细后,如何统计这连续7天的连续登录用户总数? 我们使用当天的日期作为 Bitmap 键,使用 userId 作为偏移量。如果登录,请将偏移位置中的位设置为 1。 对应的集合中的数据位是用户当天的登录记录。 这样的位图一共有7个。如果我们可以对这7个Bitmap的相应位进行“与”运算。 相同的用户 ID 偏移量相同。如果7位图像对应的偏移位置上的某个用户ID位=1,则表示该用户已连续登录7天。 结果保存在新的位图中。然后我们统计位数=1和 Redis 提供 很容易理解,如下图所示: 3 Bitmap,将相应位进行“与”运算,并将结果保存到新的 Bitmap 中。 运算语句对三个位图进行AND运算,并将结果保存到destmap。然后对destmap进行BITCOUNT统计。 简单计算100M位图占用的内存开销大约需要12MB内存(10^8/8/1024/1024)。 7 天位图的内存约为 84 MB。不过,最好设置 Bitmap 的过期时间,让 Redis 删除过期的登录,以节省内存。 想法是最重要的。当我们遇到只需要统计数据的二进制状态的统计场景时,比如用户是否存在、IP地址是否被列入黑名单、登录统计等,我们可以考虑使用这个。位图。 只需一位即可表示0和1。这大大减少了计算大量数据时的内存使用量。Bitmap
之外的扩展数据类型。 消息
二进制状态统计
login(1)
或 未登录 ( 0 )
、 已登录 (1)
或 或 未登录。 大概的空间占用计算公式是:($offset/8/1024/1024) MB
判断用户的登录状态
GETBIT 和 SETBIT
操作,通过偏移值 offset 读写位数组的偏移位置处的位。请注意,偏移量从 0 开始。 GETBIT
判断相应用户是否在线。 5 亿用户仅需要 6 MB 空间。 SETBIT <key> <offset> <value>
GETBIT <key> <offset>
SETBIT login_status 10086 1
uid:sign:{userId}:{yyyyMM}
,该月每一天的值 - 1 可以用作偏移量(因为偏移量是从 0 开始的,所以 偏移量 = 日期 - 1
)。 SETBIT uid:sign:89757:202105 15 1
BITCOUNT
。该指令用于计算给定位块中值为 1 的位的数量。 BITCOUNT uid:sign:89757:202105
BITPOS key bitValue [start] [end]
语句。返回的数据是位图第一个值的偏移位置,即bitValue
。 start
和end
指定要检测的范围。 BITPOS uid:sign:89757:202105 1
连续登录用户总数
BITCOUNT
,得到连续7天登录的用户总数。 BITOP 操作 destkey key [key ...]
该命令用于对一个或多个 key = key 的位图进行位操作。 操作
可以是和
、OR
、、 异或
。当BITOP处理不同长度的字符串时,较短字符串的缺失部分被视为0
。空键 也被视为字符串序列
0
。 // 与操作
BITOP AND destmap bitmap:01 bitmap:02 bitmap:03
// 统计 bit 位 = 1 的个数
BITCOUNT destmap
总结
版权声明
本文仅代表作者观点,不代表Code前端网立场。
本文系作者Code前端网发表,如需转载,请注明页面地址。
发表评论:
◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。