一、应用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亿,别最初超限度了。