Redis-Bitmap介绍及使用
目录
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的优点和限制
优点
- 基于最小单位bit存储数据,非常节省空间,大概的空间占用计算公式是:($offset/8/1024/1024)MB,比如我有5000W用户,那么一天的数据大约为50000000/8/1024/1024=6MB
- 设置数据的时间复杂度O(1) 获取数据的时间复杂度 也是 O(1),所以操作很快
- 方便扩容
限制
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介绍及使用相关推荐
- Redis 数据类型介绍
Redis 数据类型介绍 你也许已经知道Redis并不是简单的key-value存储,实际上他是一个数据结构服务器,支持不同类型的值.也就是说,你不必仅仅把字符串当作键所指向的值.下列这些数据类型都可 ...
- Redis 学习笔记-NoSQL数据库 常用五大数据类型 Redis配置文件介绍 Redis的发布和订阅 Redis_事务_锁机制_秒杀 Redis应用问题解决 分布式锁
1.NoSQL数据库 1.1 NoSQL数据库概述 NoSQL(NosQL = Not Only sQL ),意即"不仅仅是sQL",泛指非关系型的数据库.NoSQL不依赖业务逻辑 ...
- Redis bitmap、hyperlog、布隆过滤器、RoaringBitmap原理应用场景与日活的统计的具体应用
传统方案-mysql 缺点: 1.空间占用大 2.统计逻辑复杂,比如 统计最近 30 天用户的累计活跃天(每个用户在 30 天里有 N 天使用 app,N 为 1-30,然后将月活跃用户的 N 天加总 ...
- 第3节-Redis数据类型介绍以及应用
第3节-Redis数据类型介绍以及应用 1.9大类型 String(字符类型) Hash(散列类型) List(列表类型) Set(集合类型) SortedSort(有序集合类型,简称zset) Bi ...
- Redis的介绍和使用
1. NoSQL 数据库简介 1.1. 技术发展 就不讲了 1.2. NoSQL数据库 1.2.1. NoSQL数据库概述 NoSQL(NoSQL = Not Only SQL ),意即"不 ...
- redis练习手册redis的配置文件redis.conf介绍
如果希望使用 redis.conf 启动 redis 需要在启动 redis-server 后加上 redis.conf ,否则会使用默认配置启动 reids ./src/redis-server r ...
- Redis 配置文件介绍——redis.conf
Units单位 # Note that in order to read the configuration file, Redis must be # started with the file p ...
- Redis Cluster 介绍与搭建
1. Redis Cluster介绍 Redis Cluster是Redis的分布式解决方案,在Redis 3.0版本正式推出的,有效解决了Redis分布式方面的需求.当遇到单机内存.并发.流量等瓶颈 ...
- 三大缓存框架ehcache、memcache和redis的介绍
三大缓存框架ehcache.memcache和redis的介绍 2016-04-12 架构说 4964 阅读 最近项目组有用到这三个缓存,去各自的官方看了下,觉得还真的各有千秋!今天特意归纳下各个缓存 ...
- NoSQL数据库之Redis数据库:Redis的介绍与安装部署(redis-2.8.19/3.2.5)
NoSQL(NoSQL = Not Only SQL),它指的是非关系型的数据库.随着互联网web2.0网站的兴起,传统的关系数据库在应付web2.0网站,特别是超大规模和高并发的SNS类型的w ...
最新文章
- 通向架构师的道路(第五天)之tomcat集群-群猫乱舞
- LiveVideoStack线上分享第三季(四):计算存储在视频转码中的应用
- 没想到咱也算得上是先富起来的那一拨
- HTML5新布局元素布局,HTML5新的布局元素
- Fiddler (二) : Script 的 用法
- 嵌套地狱_解决嵌套业务逻辑_使用Js的对象_避免数组嵌套---SpringCloud Alibaba_若依微服务框架改造_ElementUI---工作笔记016
- UVA11424 GCD - Extreme (I)【欧拉函数打表】
- sqlplus 命令登录 Oracle
- 把书本上的字快速弄到电脑上
- 马克维茨组合matlab,马克维茨投资组合模型的matlab计算.pdf
- php富强民主,给你的网站添加“富强民主”点击特效
- Arch 使用 i3 美化桌面
- go语言实现where in查询
- 哺乳期这几种还真的要少吃,不能吃的食物真有那么多?
- 如何通过name获取单选框和复选框选中状态的value值?
- 魅族手机云便签的这些使用小技巧 你都知道吗?
- 蓝桥杯复数运算python
- uefi启动 多硬盘gtp_关于UEFI启动+GPT分区的一些经验
- ReactiveUI 入门
- Electron和Vue
热门文章
- java中位操作_Java中使用位操作的几个小技巧
- Tomcat 项目代码上线步骤详解
- linux 文件重命名或文件移动
- linux 安全防护管理
- 数据库备份和恢复秩序的关系(周围环境:Microsoft SQL Server 2008 R2)
- LVM逻辑卷管理基本概念及原理
- 帐户当前被锁定,所以用户 sa 登录失败。系统管理员无法将该帐户解锁 解决方法...
- 34.Silverlight中不得不了解使用的依赖属性
- 搞懂nginx的proxy模块-01
- 高斯消元(二)——竞赛题目中异或和的高斯消元