一、应用 redis-bitmap 背景和劣势
1. 我的项目背景
- 最近做的需要中,有个每日签到性能,须要用户每日签到,并且统计累计签到了多少天等这样的信息。
- 在思考我的项目计划的时候,最简略的就是 mysql,记录下用户每天签到的数据,每个用户每天签到的时候都 insert 一条数据,利用 mysql 统计起来也超级不便,然而;
- 思考一个问题,一个用户一年的签到数据将是 365 条,如果用户数大的话,将是 365* 用户数这么多条记录,这些记录仅仅是一个状态数据,一个签到状态实际上用“1”或者“0”这样的数字就能齐全示意进去,那么有没有更好更节俭存储空间的形式呢?
- 实际上,遇到这种只须要存储“是”或者“否”状态的数据的时候,能够尝试是否能够应用 bitmap 这种数据结构来存储数据。
2. 什么是 bitmap
- bitmap 能够设想成一个围棋盘这种点位阵,每个格子代表一个 bit 位,只能存储 1 或者 0 两种数据。
- 因而,bitmap 应用场景实际上就是只能存储“状态”类型数据,比方咱们将在我的项目中用到的签到数据,因为签到状态只有两种,一种已签到能够用“1”示意,一种未签到能够用“0”示意。
3.bitmap 劣势
- bitmap 最大的劣势就是能够存储海量数据并且疾速检索到具体的数据。
- 咱们再回到之前说的签到性能的 mysql 计划,一个用户一天的签到数据蕴含 uid、sign_status、created_at,最起码三个 int 类型字段,一个 int 类型是 4 个字节,三个 int 就是 12 个字节,也就是一个用户一天的数据最低占 12 个字节,千万用户一年的数据是 1000w x 365 x 12=438000w 字节 =42773M,请记住这个 42773M,是 mysql 存储占用的空间,大概 41G。
- 那么咱们来计算一下用 bitmap 存储千万用户一年的数据占据多少空间。如果咱们用户 uid 大部分是间断的,那么每天所有的用户签到数据都存在一个 key,比方 uid=1000 的用户签到,就将这个 key 的第 1000 位设置为 1,这样实际上所有用户一年的数据只有 365 条,一条数据保留 1000w 个 bit,因为一个字节 8 个 bit,就是 125w 字节,那么千万用户一年的数据是 125w x 365=45625w 字节 =445M。
- 以上,咱们晓得,应用 mysql 保留一年千万用户最低 42773M 空间,而应用 bitmap 保留一年千万用户最低 445M,bitmap 空间只占了 mysql 的 1%,这个差异真的是天差地别。
- 另外 bitmap 查问数据的时候,redis 提供了相干命令能够间接统计一个 key 中有多少个 1,也能够间接获取到具体那个位 bit 的值。
- 其中,在一台 2010MacBook Pro 上,offset 为 2^32-1(调配 512MB)须要~300ms,offset 为 2^30-1(调配 128MB)须要~80ms,offset 为 2^28-1(调配 32MB)须要~30ms,offset 为 2^26-1(调配 8MB)须要 8ms。所以查问某个位的值的时候速度也是相当快,redis 中一个 string 类型的 key 限度不能超过 512M。
二、redis-bitmap 相干命令介绍
-
Setbit
语法:setbit key offset value 形容:对 key 所贮存的字符串值,设置或革除指定偏移量上的位(bit)。位的设置或革除取决于 `value` 参数,能够是 `0` 也能够是 `1`。当 `key` 不存在时,主动生成一个新的字符串值。字符串会进行舒展 (grown) 以确保它能够将 `value` 保留在指定的偏移量上。当字符串值进行舒展时,空白地位以 `0` 填充。留神:`offset` 参数必须大于或等于 `0`,小于 2^32 (bit 映射被限度在 512 MB 之内)。因为 Redis 字符串的大小被限度在 512 兆 (megabytes) 以内,所以用户可能应用的最大偏移量为 2^29-1(536870911),如果你须要应用比这更大的空间,请应用多个 `key。` 当生成一个很长的字符串时,Redis 须要分配内存空间,该操作有时候可能会造成服务器阻塞 (block)。在 2010 年出产的 Macbook Pro 上,设置偏移量为 536870911(512MB 内存调配) 将消耗约 300 毫秒,设置偏移量为 134217728(128MB 内存调配)将消耗约 80 毫秒,设置偏移量 33554432(32MB 内存调配)将消耗约 30 毫秒,设置偏移量为 8388608(8MB 内存调配)将消耗约 8 毫秒。
-
getbit
语法:getbit key offset 形容:对 key 所贮存的字符串值,获取指定偏移量上的位(bit)。当 offset 比字符串值的长度大,或者 key 不存在时,返回 0
-
bitcount
语法:bitcount key [start] [end] 返回值:被设置为 1 的位的数量 形容:计算给定字符串中,被设置为 1 的比特位的数量 个别状况下,给定的整个字符串都会被进行计数,通过指定额定的 start 或 end 参数,能够让计数只在特定的位上进行。start 和 end 参数的设置和 GETRANGE key start end 命令相似,都能够应用负数值:比方 - 1 示意最初一个 bit,-2 示意倒数第二个 bit,以此类推。不存在的 key 被当成是空字符串来解决,因而对一个不存在的 key 进行 BITCOUNT 操作,后果为 0。
-
bitpos
语法:bitpos key bit [start] [end] 返回值:返回字符串外面第一个被设置为 1 或者 0 的 bit 位。形容:返回一个地位,把字符串当做一个从左到右的字节数组,第一个符合条件的在地位 0,其次在地位 8,等等。默认状况下整个字符串都会被检索一次,只有在指定 start 和 end 参数(指定 start 和 end 位是可行的),该范畴被解释为一个字节的范畴,而不是一系列的位。所以 start=0 并且 end= 2 是指前三个字节范畴内查找。
-
bitop
语法:bitop operation destkey key [key ...] operation 能够是 AND、OR、NOT、XOR 这四种操作中的任意一种:BITOP AND destkey key [key ...],对一个或多个 key 求逻辑并,并将后果保留到 destkey。BITOP OR destkey key [key ...],对一个或多个 key 求逻辑或,并将后果保留到 destkey。BITOP XOR destkey key [key ...],对一个或多个 key 求逻辑异或,并将后果保留到 destkey。BITOP NOT destkey key,对给定 key 求逻辑非,并将后果保留到 destkey。除了 NOT 操作之外,其余操作都能够承受一个或多个 key 作为输出。返回值:保留到 destkey 的字符串的长度,和输出 key 中最长的字符串长度相等 形容:对一个或多个保留二进制位的字符串 key 进行位元操作,并将后果保留到 destkey 上。留神:解决不同长度的字符串 当 BITOP 解决不同长度的字符串时,较短的那个字符串所短少的局部会被看作 0。空的 key 也被看作是蕴含 0 的字符串序列。
-
bitfield
语法:bitfield key [GET type offset] [SET type offset value] [INCRBY type offset increment] [OVERFLOW WRAP|SAT|FAIL] 形容:该命令将 Redis 字符串视为一个位数组,并且可能解决具备不同位宽和任意非(必要)对齐偏移量的特定整数字段。实际上,应用此命令能够将位偏移量为 1234 的带符号 5 位整数设置为特定值,从偏移量 4567 中检索 31 位无符号整数。相似地,该命令解决指定整数的递增和递加,提供保障和良好指定的溢出和下溢行为,用户能够配置。留神:详情能够查看文章:https://cloud.tencent.com/developer/section/1374165
三、bitmap 利用场景
1. 用户每日签到
- 用户每日签到,能够应用 redis-bitmap,对于 key 的保留形式,有不同计划,一种是一个 key 保留一天内所有用户的签到状态,这种形式留神,如果大部分签到的用户 uid 并不间断,那么整个 key 看到将会有大量的 00000000,只有零星的 1,所以这种形式实用于间断 uid 数据保留;
- 另一种 key 存值形式,就是以 key=uid_year_month 保留一个用户一个月的签到数据,这种适宜用户量少的形式。
2. 统计用户沉闷用户
- 应用工夫作为 key,用户 uid 作为 offset,沉闷过就设置为 1
- 遇到同样的问题,就是 uid 不间断怎么办?
- 这种形式能够思考分片保留:例如 uid 对 1000 取模失去的数都是小于 1000 的,uid 小于 1000 的作为分片 1,uid 在 1000~2000 作为分片 2,那么能够设置 key= 分片 id,具体的位就是 uid 对 1000 取模失去的值,这样保留的时候只是须要先确定以后 uid 在哪个分片即可。
四、思考及其他留神的点
- 具体的 bitmap 保留形式,要尽量思考本人业务状况,通过计算具体哪种形式保留更节俭空间来确定。
- redis 中一个 key 限度不能大于 512M,bit 位个数尽量小于 2^29-1(536870911),大概 5 亿,别最初超限度了。