目录

1、Bitmap是什么

2、Bitmap 基本命令

3、Bitmap的优点和限制

4、Bitmap使用场景

4.1、引入依赖、配置

4.2、活跃用户

4.3、查询指定日期 活跃的用户数

4.4、扩展 周活跃用户数

4.5、用户/员工签到

总结


1、Bitmap是什么

可以把BitMap想象成一个数组,树组的下标即是 偏移量,数组只能存储 0 1。

bitmap = 位图,就是 byte 数组,用二进制表示,这个数组只能存储0或者1 。bitmap 就是用最小的单位bit来存储 0/1 从而表示某个元素对应的值或者状态。

2、Bitmap 基本命令

setbit key offset value:对key 所存储的字符串值,设置或清除指定偏移量上的位(bit)

getbit key offset : 对key所存储的字符串值,获取指定偏移量上的位(bit)

bitcount key [offset_start, offset_end] : 获取位图指定范围中位值为1的个数如果不指定start与end,则取所有。

bitop op destkey [key1,key2...] : 做多个bitmap的and(交集)、or(并集)、not(非)、xor(异或)操作并将结果保存在destKey中

3、Bitmap的优点和限制

优点

  1. 基于最小单位bit存储数据,非常节省空间,大概的空间占用计算公式是:($offset/8/1024/1024)MB,比如我有5000W用户,那么一天的数据大约为50000000/8/1024/1024=6MB
  2. 设置数据的时间复杂度O(1) 获取数据的时间复杂度 也是 O(1),所以操作很快
  3. 方便扩容

限制

bit 映射被限制在512MB之内,所以最大是 2^32次方 ,因为 1M = 1024kb  ,  1kb = 1024 B  , 1B = 8bit。

所以1M = 1024*1024 * 8   = 2^23     那么512MB = 2^32 次方 ,所以建议 key的 offset 控制一下。

4、Bitmap使用场景

根据上述了解到的理论知识可知,bitmap可以记录一些状态,通过0 1 区分状态的场景。下面就通过小案例展示 如果通过 RedisTemplate 操作 Bitmap。

1.RedisTemplate 使用opsForValue 操作 setbit和getbit

2.RedisTemplate 没有提供直接操作 bitcount的方法,通过 redisTemplate.execute

来执行bitcount方法

4.1、引入依赖、配置

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>   <groupId>org.springframework.boot</groupId>   <artifactId>spring-boot-starter-web</artifactId>
</dependency>
@Configuration public class RedisConfig {
@Bean    public RedisTemplate<String, String> redisTemplate(RedisConnectionFactory factory) {        StringRedisTemplate template = new StringRedisTemplate(factory);   Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);  StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();  ObjectMapper om = new ObjectMapper();       om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);       om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);       jackson2JsonRedisSerializer.setObjectMapper(om);      template.setKeySerializer(stringRedisSerializer);     template.setHashKeySerializer(stringRedisSerializer);        template.setValueSerializer(stringRedisSerializer);       template.setHashValueSerializer(stringRedisSerializer);      template.afterPropertiesSet();
return template;
} }

4.2、活跃用户

我们假设某一天登录过 就算 活跃 ,那么可以登录后设置 状态为 1 ; key = 2021-06-26

@GetMapping("/login") public void login(String username, String password) {
//1.模拟从数据库查询 用户信息
UserEntity user = findUserByUsername(username);
if (user != null) {
//记录今日活跃       recordActivity(user.getId());
}
//其他业务逻辑
}
private Boolean recordActivity(long id) {
//获取今日日期
LocalDate date = LocalDate.now();       String dateText = date.format(DateTimeFormatter.ISO_DATE);  return redisTemplate.opsForValue().setBit(dateText, id, true);
}

4.3、查询指定日期 活跃的用户数

RedisTemplate 没有提供直接操作 bitcount的方法,通过redisTemplate.execute

来执行bitcount方法

/** * 根据日期 查询指定日期的活跃用户数 * * @param dateText */@GetMapping("/day") public Long dayActivity(String dateText) {   return (Long) redisTemplate.execute((RedisCallback<Long>) cn -> cn.bitCount(dateText.getBytes()));
//其他业务逻辑 }

4.4、扩展 周活跃用户数

通过上面的基础,我们可以配合 bitop  操作 来统计 一周的 活跃用户数  然后通过 bitcount 计算出来,命令如下所示:

bitop  or  weekActivityKey  2021-04-26   2021-04-27  2021-04-28  2021-04-29  2021-04-30  2021-05-01  2021-05-02

bitcount  weekActivityKey    ## 即可得到 近一周的 活跃用户数的统计结果


@GetMapping("/week") public Long weekActivity() {    List<String> weekDateList = getWeekDateList();    String result = "";    //读取result 的 结果    return (Long) redisTemplate.execute((RedisCallback<Long>) redisConnection -> {        RedisStringCommands redisStringCommands = redisConnection.stringCommands();        List<byte[]> collect = weekDateList.stream().map(String::getBytes).collect(Collectors.toList());        //collect.toArray(new byte[][]{new byte[collect.size()]}        //先通过 or 操作 计算结果到 result        redisStringCommands.bitOp(BitOperation.OR, result.getBytes(), collect.toArray(new byte[][]{new byte[collect.size()]}));        return redisConnection.bitCount(result.getBytes());   }); } //查询 最近一周日期list private List<String> getWeekDateList() {     List<String> weekList = new ArrayList<>();     SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");     Calendar c = Calendar.getInstance();     // 今天是一周中的第几天     int dayOfWeek = c.get(Calendar.DAY_OF_WEEK);     if (c.getFirstDayOfWeek() == Calendar.SUNDAY) {            c.add(Calendar.DAY_OF_MONTH, 1);     }     // 计算一周开始的日期     c.add(Calendar.DAY_OF_MONTH, -dayOfWeek);     for (int i = 1; i <= 7; i++) {          c.add(Calendar.DAY_OF_MONTH, 1);          weekList.add(sdf.format(c.getTime()));     }     return weekList; }

4.5、用户/员工签到

签到也是可以通过 0 1 状态来记录。假设公司对100个员工进行签到行为统计,可以为当月每一天分配一个bitmap,这个bitmap保存100个bit位,来记录签到行为。

/** * 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); }

可以查看某一天的打卡总人数,这样就能根据打卡人数来判断当天的迟到人数比例。

/** * BITCOUNT命令 * 查看某一天的打卡人数 * 时间复杂度:O(N) */ public Long signedCount(String key){ return (Long) redisTemplate.execute((RedisCallback<Long>) conn -> conn.bitCount(key.getBytes())); }

如果想看当月没有迟到过的员工呢?就需要用到交集了,对当月每天的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((RedisCallback<Long>) conn -> conn.bitCount(andMap.getBytes())); }

下面给出测试代码,模拟只有50个员工全勤。

@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); }

总结

bitmap广泛地运用在二值计算的场景,对于一个二值状态只用一个bit位就可以,非常节约内存。比如我们对一个10亿的用户进行日活计算,占用的空间只有120M:

10亿/8/1024/1024=120M

Redis-Bitmap介绍及使用相关推荐

  1. Redis 数据类型介绍

    Redis 数据类型介绍 你也许已经知道Redis并不是简单的key-value存储,实际上他是一个数据结构服务器,支持不同类型的值.也就是说,你不必仅仅把字符串当作键所指向的值.下列这些数据类型都可 ...

  2. Redis 学习笔记-NoSQL数据库 常用五大数据类型 Redis配置文件介绍 Redis的发布和订阅 Redis_事务_锁机制_秒杀 Redis应用问题解决 分布式锁

    1.NoSQL数据库 1.1 NoSQL数据库概述 NoSQL(NosQL = Not Only sQL ),意即"不仅仅是sQL",泛指非关系型的数据库.NoSQL不依赖业务逻辑 ...

  3. Redis bitmap、hyperlog、布隆过滤器、RoaringBitmap原理应用场景与日活的统计的具体应用

    传统方案-mysql 缺点: 1.空间占用大 2.统计逻辑复杂,比如 统计最近 30 天用户的累计活跃天(每个用户在 30 天里有 N 天使用 app,N 为 1-30,然后将月活跃用户的 N 天加总 ...

  4. 第3节-Redis数据类型介绍以及应用

    第3节-Redis数据类型介绍以及应用 1.9大类型 String(字符类型) Hash(散列类型) List(列表类型) Set(集合类型) SortedSort(有序集合类型,简称zset) Bi ...

  5. Redis的介绍和使用

    1. NoSQL 数据库简介 1.1. 技术发展 就不讲了 1.2. NoSQL数据库 1.2.1. NoSQL数据库概述 NoSQL(NoSQL = Not Only SQL ),意即"不 ...

  6. redis练习手册redis的配置文件redis.conf介绍

    如果希望使用 redis.conf 启动 redis 需要在启动 redis-server 后加上 redis.conf ,否则会使用默认配置启动 reids ./src/redis-server r ...

  7. Redis 配置文件介绍——redis.conf

    Units单位 # Note that in order to read the configuration file, Redis must be # started with the file p ...

  8. Redis Cluster 介绍与搭建

    1. Redis Cluster介绍 Redis Cluster是Redis的分布式解决方案,在Redis 3.0版本正式推出的,有效解决了Redis分布式方面的需求.当遇到单机内存.并发.流量等瓶颈 ...

  9. 三大缓存框架ehcache、memcache和redis的介绍

    三大缓存框架ehcache.memcache和redis的介绍 2016-04-12 架构说 4964 阅读 最近项目组有用到这三个缓存,去各自的官方看了下,觉得还真的各有千秋!今天特意归纳下各个缓存 ...

  10. NoSQL数据库之Redis数据库:Redis的介绍与安装部署(redis-2.8.19/3.2.5)

     NoSQL(NoSQL = Not Only SQL),它指的是非关系型的数据库.随着互联网web2.0网站的兴起,传统的关系数据库在应付web2.0网站,特别是超大规模和高并发的SNS类型的w ...

最新文章

  1. 通向架构师的道路(第五天)之tomcat集群-群猫乱舞
  2. LiveVideoStack线上分享第三季(四):计算存储在视频转码中的应用
  3. 没想到咱也算得上是先富起来的那一拨
  4. HTML5新布局元素布局,HTML5新的布局元素
  5. Fiddler (二) : Script 的 用法
  6. 嵌套地狱_解决嵌套业务逻辑_使用Js的对象_避免数组嵌套---SpringCloud Alibaba_若依微服务框架改造_ElementUI---工作笔记016
  7. UVA11424 GCD - Extreme (I)【欧拉函数打表】
  8. sqlplus 命令登录 Oracle
  9. 把书本上的字快速弄到电脑上
  10. 马克维茨组合matlab,马克维茨投资组合模型的matlab计算.pdf
  11. php富强民主,给你的网站添加“富强民主”点击特效
  12. Arch 使用 i3 美化桌面
  13. go语言实现where in查询
  14. 哺乳期这几种还真的要少吃,不能吃的食物真有那么多?
  15. 如何通过name获取单选框和复选框选中状态的value值?
  16. 魅族手机云便签的这些使用小技巧 你都知道吗?
  17. 蓝桥杯复数运算python
  18. uefi启动 多硬盘gtp_关于UEFI启动+GPT分区的一些经验
  19. ReactiveUI 入门
  20. Electron和Vue

热门文章

  1. java中位操作_Java中使用位操作的几个小技巧
  2. Tomcat 项目代码上线步骤详解
  3. linux 文件重命名或文件移动
  4. linux 安全防护管理
  5. 数据库备份和恢复秩序的关系(周围环境:Microsoft SQL Server 2008 R2)
  6. LVM逻辑卷管理基本概念及原理
  7. 帐户当前被锁定,所以用户 sa 登录失败。系统管理员无法将该帐户解锁 解决方法...
  8. 34.Silverlight中不得不了解使用的依赖属性
  9. 搞懂nginx的proxy模块-01
  10. 高斯消元(二)——竞赛题目中异或和的高斯消元