bitmap
1.简介
Bitmap实际上就是String类型,通过最小的单位bit来进行0或者1的设置,表示某个元素对应的值或者状态。
一个bit的值,或者是0,或者是1
redis 字符串最大值为512M,所以bigmap最大值为:4294967295
8 * 1024 * 1024 * 512 = 2^32 = 4294967296-1 = 4294967295
2.使用场景
- 用户在线状态
- 统计活跃用户
- 用户签到
- …
3.bitmap的基本使用 更多命令
setbit : 给一个指定key的值得第位offset 赋值为value。
getbit : 返回一个指定key的二进制信息
setbit k1 0 1 #设置第1位为1 0111 0111
getbit k1 1 #获取第一位的值
bitfield:批量操作
i 有符号数
指获取的位数组中第一个位是符号位,剩下的才是值。如果第一位是1,那就是负数。
u 无符号数
表示非负数,没有符号位,获取的位数组全部都是值。
原码 原码,反码,补码
# 表示正负(0正,1负)
# 原码就是符号位加上真值的绝对值, 即用第一位表示符号, 其余位表示值
[+1]原 = 0001
[-1]原 = 1001
反码
# 正数的反码是其本身
# 负数的反码是在其原码的基础上, 符号位不变,其余各个位取反
[+1] = [0001]原 = [0001]反
[-1] = [1001]原 = [1110]反
补码
# 正数的补码就是其本身
# 负数的补码是在其原码的基础上, 符号位不变, 其余各位取反, 最后+1. (即在反码的基础上+1)
[+1] = [00000001]原 = [00000001]反 = [00000001]补
[-1] = [10000001]原 = [11111110]反 = [11111111]补
# set 设置指定位域的值
# 33 0010 0001 37 0010 0101
# kx:key, i/u x:替换接下来的x个位, 0:从第一个位开始, 33:十进制数
bitfield k2 set i8 0 33 # 从第0个位开始,将接下来的 8个位用无符号数33替换 0010 0001 !
bitfield k3 set i4 0 37 # 从第4个位开始,将接下来的 4个位用无符号数37替换 0101 0000 P
bitfield k4 set u6 1 37 # 从第1个位开始,将接下来的 6个位用无符号数37替换 0100 1010 J
# get 返回指定的位域
# kx:key, i/u x:替换接下来的x个位, 2:从第二个位开始
bitfield k2 get u6 2 # 从第二个位开始取 6 个位,结果是无符号数 (u)
bitfield k2 get i6 2 # 从第二个位开始取 6 个位,结果是有符号数 (i)
# incrby (正数自增,负数自减)
# k3 = 0101 0000 P + 2 = 0101 0010 = R
bitfield k3 incrby u4 4 2 # 从第四个位开始,对接下来的 4 位无符号数 +2
bitcount : 返回一个指定key中位的值为1的个数 (字节为单位)
# k4 = 0100 1010 J o1 = 0000 0011 0001 0010
bitcount k4 # 获取k4中出现的1的个数
bitcount o1 0 0 # 获取o1第一个字节中出现的1的个数
bitpos : 用来查找指定范围内出现的第一个 0 或 1 (字节为单位)
#k6 = abc = 0110 0001 0110 0010 0110 0011
bitpos k6 1 1 1 # 获取k6第一,第一个1的位置
bitop : 对一个或多个保存二进制位的字符串 key 进行位元操作,并将结果保存到 destkey 上
BITOP 命令支持 AND 、 OR 、 NOT 、 XOR 这四种操作
AND: 与运算符(&) 两个同时为1,结果为1,否则为0
OR: 或运算(|) 一个为1,其值为1
NOT: 取反(0110 0001 NOT: 1001 1110)
XOR: 异或运算,值不同为1,否则为0
4.案例实现 (统计网站活跃度)
该网站一天有多少活跃用户 ?
该网站一周有多少活跃用户 ?
该网站一月有多少活跃用户 ?
...
统计活跃用户:
USER_A用户 : 连续一周每天都有登录系统
USER_B用户 : 只在"2021-05-22"登录系统
USER_C用户 : 一周有5天登录系统
public static final Long USER_A = 123456L;
public static final Long USER_B = 2L;
public static final Long USER_C = 3L;
public static final String DAY_01 = "2021-05-21";
public static final String DAY_02 = "2021-05-22";
public static final String DAY_03 = "2021-05-23";
public static final String DAY_04 = "2021-05-24";
public static final String DAY_05 = "2021-05-25";
public static final String DAY_06 = "2021-05-26";
public static final String DAY_07 = "2021-05-27";
/**
* 统计活跃用户
*/
@Test
void test() {
// String today = DateUtil.today();
redisTemplate.opsForValue().setBit(DAY_01, USER_A, true);
redisTemplate.opsForValue().setBit(DAY_02, USER_A, true);
redisTemplate.opsForValue().setBit(DAY_02, USER_B, true);
redisTemplate.opsForValue().setBit(DAY_02, USER_C, true);
redisTemplate.opsForValue().setBit(DAY_03, USER_A, true);
redisTemplate.opsForValue().setBit(DAY_03, USER_C, true);
redisTemplate.opsForValue().setBit(DAY_04, USER_A, true);
redisTemplate.opsForValue().setBit(DAY_04, USER_C, true);
redisTemplate.opsForValue().setBit(DAY_05, USER_A, true);
redisTemplate.opsForValue().setBit(DAY_05, USER_C, true);
redisTemplate.opsForValue().setBit(DAY_06, USER_A, true);
redisTemplate.opsForValue().setBit(DAY_06, USER_C, true);
redisTemplate.opsForValue().setBit(DAY_07, USER_A, true);
countTodayUser(DAY_02);
countWeekUser();
}
/**
* 统计当日登陆用户个数
*
* @param date
*/
void countTodayUser(String date) {
System.out.println(date + ":" + "登陆的用户个数 :" + redisTemplate.execute((RedisCallback<Long>) connection -> connection.bitCount((date).getBytes())));
}
/**
* 统计一周内登陆用户的个数
*/
void countWeekUser() {
redisTemplate.execute((RedisCallback<Long>) connection -> connection.bitOp(RedisStringCommands.BitOperation.AND,"count-week-user-and".getBytes(),
DAY_01.getBytes(), DAY_02.getBytes(), DAY_03.getBytes(),DAY_04.getBytes(), DAY_05.getBytes(), DAY_06.getBytes(), DAY_07.getBytes()));
redisTemplate.execute((RedisCallback<Long>) connection -> connection.bitOp(RedisStringCommands.BitOperation.OR, "count-week-user-or".getBytes(),
DAY_01.getBytes(), DAY_02.getBytes(), DAY_03.getBytes(), DAY_04.getBytes(), DAY_05.getBytes(), DAY_06.getBytes(), DAY_07.getBytes()));
System.out.println("一周内每天都登陆的用户个数 :" + redisTemplate.execute((RedisCallback<Long>) connection -> connection.bitCount("count-week-user-and".getBytes())));
System.out.println("一周内登陆过的用户个数 :" + redisTemplate.execute((RedisCallback<Long>)
connection > connection.bitCount("count-week-user-or".getBytes())));
}
// 运行结果
2021-05-22:登陆的用户个数 :3
一周内每天都登陆的用户个数 :1
一周内登陆过的用户个数 :3