이 글은Redis의 실제적인 부분입니다. Redis+Bitmap을 사용하여 10억 단위의 대용량 데이터 통계를 달성하는 방법을 소개하는 글입니다.
모바일 애플리케이션의 비즈니스 시나리오에서는 이러한 정보를 저장해야 합니다. 키는 데이터 수집과 연결되어 있습니다. [관련 권장 사항:Redis 비디오 튜토리얼]
일반적인 시나리오는 다음과 같습니다:
일반적으로 우리가 직면하는 사용자 수와 방문 수는 엄청납니다. 수백만, 수천만 명의 사용자, 수천만 명, 심지어는 수십억 수준의 접속 정보까지.
그래서 우리는 대량의 데이터(예: 수십억)를 매우 효율적으로 계산할 수 있는 컬렉션 유형을 선택해야 합니다.
적절한 데이터 세트를 선택하는 방법은 무엇입니까? 먼저 일반적으로 사용되는 통계 모델을 이해하고 합리적인 데이터 이해를 통해 실질적인 문제를 해결해야 합니다.
4가지 통계 유형:
집계 통계
카디널리티 통계;
이 기사에서는
Binary 상태 통계 유형Bitmap
이외의 확장 데이터 유형을 사용합니다. > 구현합니다.
Bitmap
来实现。
文章涉及到的指令可以通过在线 Redis 客户端运行调试,地址:try.redis.io/,超方便的说。
多分享多付出,前期多给别人创造价值并且不计回报,从长远来看,这些付出都会成倍的回报你。
特别是刚开始跟别人合作的时候,不要去计较短期的回报,没有太大意义,更多的是锻炼自己的视野、视角以及解决问题的能力。
码哥,什么是二值状态统计呀?
也就是集合中的元素的值只有 0 和 1 两种,在签到打卡和用户是否登陆的场景中,只需记录签到(1)
或未签到(0)
,已登录(1)
或未登陆(0)
Ma 형제님, 이진 상태 통계가 무엇인가요?
즉, 컬렉션에 포함된 요소의 값은 0과 1뿐입니다. 체크인과 체크인 및 사용자 로그인 여부를 나타내는 시나리오에서는key -> userId, 값 -> 0은 오프라인, 1 - 로그인Sign만 기록하면 됩니다. -in(1)
또는체크인하지 않음(0)
,로그인함(1)
또는로그인하지 않음(0)
코드>. 사용자가 로그인했는지 여부를 결정하는 시나리오에서 Redis의 문자열 유형 구현을 사용한다고 가정합니다(
을 의미함). 100만 명의 사용자 상황에서 문자열 형태로 저장한다면 100만 개의 문자열을 저장해야 하므로 메모리를 너무 많이 소모하게 됩니다.
엄마 형제님, 문자열 유형의 메모리 오버헤드가 높은 이유는 무엇인가요? 문자열 유형은 실제 데이터를 기록하는 것 외에도 데이터 길이, 공간 사용량 및 기타 정보를 기록하기 위해 추가 메모리가 필요합니다.buf: 바이트 배열, 실제 데이터 저장, Redis는 자동으로 배열 끝에 "를 추가합니다.
Bitmap 的底层数据结构用的是 String 类型的 SDS 数据结构来保存位数组,Redis 把每个字节数组的 8 个 bit 位利用起来,每个 bit 位 表示一个元素的二值状态(不是 0 就是 1)。
可以将 Bitmap 看成是一个 bit 为单位的数组,数组的每个单元只能存储 0 或者 1,数组的下标在 Bitmap 中叫做 offset 偏移量。
为了直观展示,我们可以理解成 buf 数组的每个字节用一行表示,每一行有 8 个 bit 位,8 个格子分别表示这个字节中的 8 个 bit 位,如下图所示:
8 个 bit 组成一个 Byte,所以 Bitmap 会极大地节省存储空间。这就是 Bitmap 的优势。
怎么用 Bitmap 来判断海量用户中某个用户是否在线呢?
Bitmap 提供了GETBIT、SETBIT
操作,通过一个偏移值 offset 对 bit 数组的 offset 位置的 bit 位进行读写操作,需要注意的是 offset 从 0 开始。
只需要一个 key = login_status 表示存储用户登陆状态集合数据, 将用户 ID 作为 offset,在线就设置为 1,下线设置 0。通过GETBIT
判断对应的用户是否在线。 50000 万 用户只需要 6 MB 的空间。
SETBIT 命令
SETBIT
设置或者清空 key 的 value 在 offset 处的 bit 值(只能是 0 或者 1)。
GETBIT 命令
GETBIT
获取 key 的 value 在 offset 处的 bit 位的值,当 key 不存在时,返回 0。
假如我们要判断 ID = 10086 的用户的登陆情况:
第一步,执行以下指令,表示用户已登录。
SETBIT login_status 10086 1
第二步,检查该用户是否登陆,返回值 1 表示已登录。
GETBIT login_status 10086
第三步,登出,将 offset 对应的 value 设置成 0。
SETBIT login_status 10086 0
在签到统计中,每个用户每天的签到用 1 个 bit 位表示,一年的签到只需要 365 个 bit 位。一个月最多只有 31 天,只需要 31 个 bit 位即可。
比如统计编号 89757 的用户在 2021 年 5 月份的打卡情况要如何进行?
key 可以设计成uid:sign:{userId}:{yyyyMM}
,月份的每一天的值 - 1 可以作为 offset(因为 offset 从 0 开始,所以offset = 日期 - 1
)。
第一步,执行下面指令表示记录用户在 2021 年 5 月 16 号打卡。
SETBIT uid:sign:89757:202105 15 1
第二步,判断编号 89757 用户在 2021 年 5 月 16 号是否打卡。
GETBIT uid:sign:89757:202105 15
第三步,统计该用户在 5 月份的打卡次数,使用BITCOUNT
指令。该指令用于统计给定的 bit 数组中,值 = 1 的 bit 位的数量。
BITCOUNT uid:sign:89757:202105
这样我们就可以实现用户每个月的打卡情况了,是不是很赞。
如何统计这个月首次打卡时间呢?
Redis 提供了BITPOS key bitValue [start] [end]
指令,返回数据表示 Bitmap 中第一个值为bitValue
的 offset 位置。
在默认情况下, 命令将检测整个位图, 用户可以通过可选的start
参数和end
参数指定要检测的范围。
所以我们可以通过执行以下指令来获取 userID = 89757 在 2021 年 5 月份首次打卡日期:
BITPOS uid:sign:89757:202105 1
需要注意的是,我们需要将返回的 value + 1 ,因为 offset 从 0 开始。
在记录了一个亿的用户连续 7 天的打卡数据,如何统计出这连续 7 天连续打卡用户总数呢?
我们把每天的日期作为 Bitmap 的 key,userId 作为 offset,若是打卡则将 offset 位置的 bit 设置成 1。
key 对应的集合的每个 bit 位的数据则是一个用户在该日期的打卡记录。
一共有 7 个这样的 Bitmap,如果我们能对这 7 个 Bitmap 的对应的 bit 位做 『与』运算。
同样的 UserID offset 都是一样的,当一个 userID 在 7 个 Bitmap 对应对应的 offset 位置的 bit = 1 就说明该用户 7 天连续打卡。
结果保存到一个新 Bitmap 中,我们再通过BITCOUNT
统计 bit = 1 的个数便得到了连续打卡 7 天的用户总数了。
Redis 提供了BITOP operation destkey key [key ...]
这个指令用于对一个或者多个 键 = key 的 Bitmap 进行位元操作。
opration
可以是and
、OR
、NOT
、XOR
。当BITOP处理不同长度的字符串时,较短的那个字符串所缺少的部分会被看作0
。空的key
也被看作是包含0
的字符串序列。
便于理解,如下图所示:
3 个 Bitmap,对应的 bit 位做「与」操作,结果保存到新的 Bitmap 中。
操作指令表示将 三个 bitmap 进行 AND 操作,并将结果保存到 destmap 中。接着对 destmap 执行 BITCOUNT 统计。
// 与操作 BITOP AND destmap bitmap:01 bitmap:02 bitmap:03 // 统计 bit 位 = 1 的个数 BITCOUNT destmap
简单计算下 一个一亿个位的 Bitmap占用的内存开销,大约占 12 MB 的内存(10^8/8/1024/1024),7 天的 Bitmap 的内存开销约为 84 MB。同时我们最好给 Bitmap 设置过期时间,让 Redis 删除过期的打卡数据,节省内存。
思路才是最重要,当我们遇到的统计场景只需要统计数据的二值状态,比如用户是否存在、 ip 是否是黑名单、以及签到打卡统计等场景就可以考虑使用 Bitmap。
只需要一个 bit 位就能表示 0 和 1。在统计海量数据的时候将大大减少内存占用。
原文地址:https://juejin.cn/post/6999908907791417351
作者:码哥字节
更多编程相关知识,请访问:编程视频!!
위 내용은 Redis+Bitmap을 사용하여 10억 수준의 대규모 데이터 통계를 달성하는 방법을 단계별로 안내해 드립니다.의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!