1.分布式缓存Redis之bitmap、setbit
https://blog.csdn.net/u011489043/article/details/78990162
2.Redis的位图bitmap
https://blog.csdn.net/chengqiuming/article/details/79118089
Redis 中有有一种数据结构,非常便于位图级别的统计方案。
本篇文章将会对 该种结构的 操作与 应用场景进行归纳整理。
1.简介
2.相关操作
2.1.置位/取值
2.2 计算为1的位数
2.3 与其他bitmap 进行集合操作
3.java操作bitmap
4.应用场景
位图不是真正的数据类型,它是定义在字符串类型中。
type bitMapKey -> String
一个字符串类型的值最多能存储512M字节的内容。
位上限:2^(9+10+10+3)=2^32b ,位上限指的是最多能有多少位。
2^32 还是一个很夸张的数字。
bitMap 有一系列操作,下面我们进行分类归纳
置位 :设置某一位上的值 SETBIT
取值 :获取某一位上的值 GETBIT
置位:将 key 为 user:20181210 第7,第8位设置为1
该操作的返回值 为 原来该位置的值 0 或 1
VMware_192.168.75.128:6379-redis:0>setbit usercount:20181210 7 1
"0"
查看类型:
VMware_192.168.75.128:6379-redis:0>setbit usercount:20181210 7 1
"0"
取值 : 获取 key 上某个位上的值,
VMware_192.168.75.128:6379-redis:0>getbit usercount:20181210 7
"1"
统计为bitmap 中1的个数,使用 命令 bitcount
VMware_192.168.75.128:6379-redis:0>bitcount usercount:20181210
"1"
bitcount 统计的是1的个数,
bitcount test 0 -1 就是所有的,
bitcount 0 0 那么就应该是第一个字节中1的数量的,注意是字节 第一个字节也就是 0 1 2 3 4 5 6 7 这八个位置上。
见下面的测试样例,setbit单位是bit,bitcount是以byte为间隔统计的,接上面的预设值:
第2个字节中设置为1的数量,由于设置的是第7位为1 ,所以返回 0
VMware_192.168.75.128:6379-redis:0>bitcount usercount:20181210 1 1
"0"
第1个字节中为1的数量。
VMware_192.168.75.128:6379-redis:0>bitcount usercount:20181210 0 0
"1"
redis 127.0.0.1:6379> bitop operation destkey key [key...]
//对一个或多个保存二进制位的字符串 key 进行位元操作,并将结果保存到 destkey 上
BITOP 命令支持 AND 、 OR 、 NOT 、 XOR 这四种操作中的任意一种参数:
BITOP AND destkey srckey1 … srckeyN ,对一个或多个 key 求逻辑与,并将结果保存到 destkey
BITOP OR destkey srckey1 … srckeyN,对一个或多个 key 求逻辑或,并将结果保存到 destkey
BITOP XOR destkey srckey1 … srckeyN,对一个或多个 key 求逻辑异或,并将结果保存到 destkey
BITOP NOT destkey srckey,对给定 key 求逻辑非,并将结果保存到 destkey
除了 NOT 操作之外,其他操作都可以接受一个或多个 key 作为输入,执行结果将始终保持到destkey里面。
当 BITOP 处理不同长度的字符串时,较短的那个字符串所缺少的部分会被看作 0。返回值是保存到 destkey 的字符串的长度(以字节byte为单位),和输入 key 中最长的字符串长度相等。
这里我们选用的包是市面上主流的Jedis 包,如下:
redis.clients
jedis
2.9.0
主要代码如下:
package com.redis.op;
import redis.clients.jedis.Jedis;
/**
* Created by szh on 2018/12/10.
*/
public class BitCountTest {
public static void main(String[] args) {
Jedis jedis = new Jedis("192.168.75.128", 6379);
String type = jedis.type("play:20181210");
System.out.println("key type : " + type);
Long result = jedis.bitcount("play:20181210");
System.out.println("bitcount key : " + result);
Long result2 = jedis.bitcount("play:20181210",0,0);
System.out.println("bitcount key 0 0 : " + result2);
Long result3 = jedis.bitcount("play:20181210",1,1);
System.out.println("bitcount key 1 1 : " + result3);
}
}
setbit 可以理解是一个位数组,至于这个数组有多大,redis中bit映射被限制在512MB之内,所以最大是2^32。
也就是在这样一个位数组上存0或者是1 ,可以结合bloomFilter的应用场景理解位数组的用法,将字符串使用一种合适哈希函数映射到不同的bit位上(2^32足够的大,可以满足需求)
应用场景1:
使用 bitmap 实现用户上线次数统计、统计活跃用户
假设现在我们希望记录自己网站上的用户的上线频率,比如说,计算用户 A 上线了多少天,用户 B 上线了多少天,诸如此类,以此作为数据,从而决定让哪些用户参加 beta 测试等活动 —— 这个模式可以使用 SETBIT 和 BITCOUNT 来实现。
比如说,通过将一个用户的id对应value上的一位,通过对活跃用户对应的位进行置位,就能够用一个value记录所有活跃用户的信息。如图bitmap有9个位被置为1,表示这9个位上对应的用户是今天的活跃用户。其中第15位表示uid为15的用户,第一位表示uid为0的用户。(如果你的uid不是从1开始的,比如从100000开始,实际上你也可以相应的用uid减去初始值来表示其位数,比如1000000用户对应到bitmap的第一位)
性能:
如果你的 bitmap 数据非常大,那么可以考虑使用以下两种方法:
● 将一个大的 bitmap 分散到不同的 key 中,作为小的 bitmap 来处理。使用 Lua 脚本可以很方便地完成这一工作。
● 使用 BITCOUNT 的 start 和 end 参数,每次只对所需的部分位进行计算,将位的累积工作(accumulating)放到客户端进行,并且对结果进行缓存 (caching)。
何时使用:
如果活跃用户在百万级别,使用Redis BitMap很划算。
如果活跃用户很少,而用户id都是10位以上的int。那就很浪费内存了,还不如使用set集合,然后求交集就可以了。