bitmap是redis的一种扩展数据类型主要用于二值状态统计比如公司记录员工打卡记录电商网站记录用户登录行为积分商城记录用户签到情况。bigmap底层使用的是String的数据结构而String保存在计算机中的格式是二进制的字节数组这样bitmap就充分利用了每个字节的bit位大大节省了内存开销。下面我们看一下bitmap的使用。员工打卡假如一个公司有100个员工公司要对员工11月份的打卡行为进行统计我们可以为11月份每一天分配一个bitmap这个bitmap保存100个bit位来记录员工的打卡行为。注意:bitmap偏移量从0开始所以100个bit位是从0~99依次记录1-100号员工。我们定义bitmap的key格式为:signed:20201101记录2020年11月1日的打卡情况。下面代码是员工打卡和查询员工打卡情况/** * 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((RedisCallbackLong) conn - conn.bitCount(key.getBytes())); }这样我们就能根据打卡人数来判断当天的迟到人数比例。注意上面的sign方法必须设置key的序列化采用StringRedisSerializer否则查询打卡情况是查不到的。如果不设置StringRedisSerializer上面的sign和isSigned改为使用conn来执行代码如下public void sign(String key, int employeeNumber){ redisTemplate.execute((RedisCallbackBoolean) conn - conn.setBit(key.getBytes(), employeeNumber - 1, true)); } public boolean isSigned(String key, int employeeNumber){ return redisTemplate.execute((RedisCallbackBoolean) conn - conn.getBit(key.getBytes(), employeeNumber - 1)); }或者使用下面代码来设置RedisTemplate的setKeySerializerredisTemplate.setKeySerializer(new StringRedisSerializer());那如果想看当月没有迟到过的员工呢这个时候就要用到交集了对当月每天的bitmap做交集值为1的员工就是没有迟到过的。这时就要用到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((RedisCallbackLong) 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:loginnumber为用户编号。当有用户登录时调用下面的方法redisTemplate.opsForValue().setBit(key, number - 1, true);日终的时候我们用下面的方法就可以判断出日活用户redisTemplate.execute((RedisCallbackLong) conn - conn.bitCount(key.getBytes()));总结bitmap广泛地运用在二值计算的场景对于一个二值状态只用一个bit位就可以非常节约内存。比如我们对一个10亿的用户进行日活计算占用的空间只有120M:10亿/8/1024/1024120M官网链接https://redis.io/commands/bitop
阅读完成 · 觉得有帮助?