bitmap是redis的一种扩展数据类型,主要用于二值状态统计,好比公司记录员工打卡记录,电商网站记录用户登陆行为,积分商城记录用户签到状况。redis
bigmap底层使用的是String的数据结构,而String保存在计算机中的格式是二进制的字节数组,这样bitmap就充分利用了每一个字节的bit位,大大节省了内存开销。数组
下面咱们看一下bitmap的使用。markdown
假如一个公司有100个员工,公司要对员工11月份的打卡行为进行统计,咱们能够为11月份每一天分配一个bitmap,这个bitmap保存100个bit位,来记录员工的打卡行为。数据结构
咱们定义bitmap的key格式为:signed:20201101,记录2020年11月1日的打卡状况。下面代码是员工打卡和查询员工打卡状况:ide
/** * SETBIT命令 * 员工打卡 * 时间复杂度:O(1) */ public void sign(String key, int employeeNumber){ redisTemplate.opsForValue().setBit(key, employeeNumber - 1, true); } /** * GETBIT命令 * 查看员工打卡状况 * 时间复杂度:O(1) */ public boolean isSigned(String key,int employeeNumber){ return redisTemplate.opsForValue().getBit(key, employeeNumber - 1); }
咱们能够查看某一天的打卡总人数,代码以下,入参:"signed:20201101":测试
/** * BITCOUNT命令 * 查看某一天的打卡人数 * 时间复杂度:O(N) */ public Long signedCount(String key){ return (Long) redisTemplate.execute((RedisCallback<Long>) conn -> conn.bitCount(key.getBytes())); }
这样咱们就能根据打卡人数来判断当天的迟到人数比例。网站
注意:上面的sign方法必须设置key的序列化采用StringRedisSerializer,不然查询打卡状况是查不到的。若是不设置StringRedisSerializer,上面的sign和isSigned改成使用conn来执行,代码以下:code
public void sign(String key, int employeeNumber){ redisTemplate.execute((RedisCallback<Boolean>) conn -> conn.setBit(key.getBytes(), employeeNumber - 1, true)); } public boolean isSigned(String key, int employeeNumber){ return redisTemplate.execute((RedisCallback<Boolean>) conn -> conn.getBit(key.getBytes(), employeeNumber - 1)); }
或者使用下面代码来设置RedisTemplate的setKeySerializer:内存
redisTemplate.setKeySerializer(new StringRedisSerializer());
那若是想看当月没有迟到过的员工呢?这个时候就要用到交集了,对当月天天的bitmap作交集,值为1的员工就是没有迟到过的。get
这时就要用到bitmap的聚合运算了,命令BITOP, 支持AND(与)、OR(或), XOR(异或) and NOT(非)运算,除了NOT后面跟一个bitmap外,其余3种聚合运算后面均可以跟多个bitmap,命令以下:
BITOP AND destkey srckey1 srckey2 srckey3 ... srckeyN BITOP OR destkey srckey1 srckey2 srckey3 ... srckeyN BITOP XOR destkey srckey1 srckey2 srckey3 ... srckeyN BITOP NOT destkey srckey
为了让demo简单一些,我这里给出一个查看2天内没有迟到的员工,代码以下:
/** * 命令:BITOP * 复杂度:O(N) * 整个月全勤的员工数量,这里用2天表明整个月 * @param key1 第一天 * @param key2 次日 */ public Long signedAllMonth(String key1, String key2){ String andMap = "signedAllMonth11"; redisTemplate.execute((RedisCallback) conn -> conn.bitOp(RedisStringCommands.BitOperation.AND, andMap.getBytes(), key1.getBytes(), key2.getBytes())); return (Long) redisTemplate.execute((RedisCallback<Long>) conn -> conn.bitCount(andMap.getBytes())); }
下面我再给出一段测试代码,这段代码模拟有50个员工全勤,bitMapService是我上面的代码所在类:
@Test public void testSignedAllMonth(){ for (int i = 1; i <= 100; i++){ bitMapService.sign("signed:20201101", i); } for (int i = 1; i <= 100; i += 2){ bitMapService.sign("signed:20201102", i); } Long count = bitMapService.signedAllMonth("signed:20201101", "signed:20201102"); System.out.println("=========="+count); }
输出以下:
==========50
好比网站有10万个用户,咱们要判断当天的日活用户。这样咱们建立一个长度为10万的bitmap,每一个用户id占一个位,咱们定义key为:user:login,number为用户编号。当有用户登陆时,调用下面的方法:
redisTemplate.opsForValue().setBit(key, number - 1, true);
日终的时候,咱们用下面的方法就能够判断出日活用户:
redisTemplate.execute((RedisCallback<Long>) conn -> conn.bitCount(key.getBytes()));
bitmap普遍地运用在二值计算的场景,对于一个二值状态只用一个bit位就能够,很是节约内存。好比咱们对一个10亿的用户进行日活计算,占用的空间只有120M:
10亿/8/1024/1024=120M
官网连接:
https://redis.io/commands/bitop
感谢阅读,欢迎点赞,欢迎在看,更欢迎分享给须要的朋友。