共计 4499 个字符,预计需要花费 12 分钟才能阅读完成。
【SpringBoot DB 系列】Redis 高级个性之 Bitmap 应用姿态及利用场景介绍
后面介绍过 redis 的五种根本数据结构,如 String,List, Set, ZSet, Hash,这些属于绝对常见了;在这些根本后果之上,redis 还提供了一些更高级的性能,如 geo, bitmap, hyperloglog,pub/sub,本文将次要介绍 Bitmap 的应用姿态以及其实用场景,次要知识点包含
- bitmap 根本应用
- 日活统计利用场景中 bitmap 应用姿态
- 点赞去重利用场景中 bitmap 应用姿态
- 布隆过滤器 bloomfilter 基本原理及体验 case
<!– more –>
I. 根本应用
1. 配置
咱们应用 SpringBoot 2.2.1.RELEASE
来搭建我的项目环境,间接在 pom.xml
中增加 redis 依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
如果咱们的 redis 是默认配置,则能够不额定增加任何配置;也能够间接在 application.yml
配置中,如下
spring:
redis:
host: 127.0.0.1
port: 6379
password:
2. 应用姿态
bitmap 次要就三个操作命令,setbit
,getbit
以及 bitcount
a. 设置标记
即setbit
,次要是指将某个索引,设置为 1(设置 0 示意抹去标记),根本语法如下
# 请留神这个 index 必须是数字,前面的 value 必须是 0 /1
setbit key index 0/1
对应的 SpringBoot 中,借助 RestTemplate 能够比拟容易的实现,通常有两种写法,都能够
@Autowired
private StringRedisTemplate redisTemplate;
/**
* 设置标记位
*
* @param key
* @param offset
* @param tag
* @return
*/
public Boolean mark(String key, long offset, boolean tag) {return redisTemplate.opsForValue().setBit(key, offset, tag);
}
public Boolean mark2(String key, long offset, boolean tag) {return redisTemplate.execute(new RedisCallback<Boolean>() {
@Override
public Boolean doInRedis(RedisConnection connection) throws DataAccessException {return connection.setBit(key.getBytes(), offset, tag);
}
});
}
下面两种写法的外围区别,就是 key 的序列化问题,第一种写法应用默认的 jdk 字符串序列化,和前面的 getBytes()
会有一些区别,对于这个,有趣味的小伙伴能够看一下我之前的博文: RedisTemplate 配置与应用 #序列化问题
b. 判断存在与否
即 getbit key index
,如果返回 1,示意存在否则不存在
/**
* 判断是否标记过
*
* @param key
* @param offest
* @return
*/
public Boolean container(String key, long offest) {return redisTemplate.opsForValue().getBit(key, offest);
}
c. 计数
即 bitcount key
,统计和
/**
* 统计计数
*
* @param key
* @return
*/
public long bitCount(String key) {return redisTemplate.execute(new RedisCallback<Long>() {
@Override
public Long doInRedis(RedisConnection redisConnection) throws DataAccessException {return redisConnection.bitCount(key.getBytes());
}
});
}
3. 利用场景
后面的根本应用比较简单,在介绍 String 数据结构的时候也提过,咱们重点须要关注的是 bitmap 的应用场景,它能够干嘛用,什么场景下应用它会有显著的劣势
- 日活统计
- 点赞
- bloomfilter
下面三个场景虽有相似之处,但理论的利用场景还是些许区别,接下来咱们逐个进行阐明
a. 日活统计
统计利用或网站的日活,这个属于比拟常见的 case 了,如果是用 redis 来做这个事件,首先咱们最容易想到的是 Hash 构造,个别逻辑如下
- 依据日期,设置 key,如明天为
2020/10/13
, 那么 key 能够为app_20_10_13
- 其次当用户拜访时,设置 field 为 userId, value 设置为 true
- 判断日活则是统计 map 的个数
hlen app_20_10_13
下面这个逻辑有故障么?当然没有问题,然而想一想,当咱们的利用做的很 nb 的时候,每天的日活都是百万,千万级时,这个内存开销就有点吓人了
接下来咱们看一下 bitmap 能够怎么做
- 同样依据日期设置 key
- 当用户拜访时,index 设置为 userId,
setbit app_20_10_13 uesrId 1
- 日活统计
bitcount app_20_10_13
简略比照一下下面两种计划
当数据量小时,且 userid 散布不平均,小的为个位数,大的几千万,上亿这种,应用 bitmap 就有点亏了,因为 userId 作为 index,那么 bitmap 的长度就须要能包容最大的 userId,然而理论日活又很小,阐明 bitmap 两头有大量的空白数据
反之当数据量很大时,比方百万 / 千万,userId 是间断递增的场景下,bitmap 的劣势有两点:1. 存储开销小,2. 统计总数快
c. 点赞
点赞的业务,最次要的一点是一个用户点赞过之后,就不能持续点赞了(当然某些业务场景除外),所以咱们须要晓得是否能够持续点赞
下面这个 hash 当然也能够实现,咱们这里则次要讨论一下 bitmap 的实现逻辑
- 比方咱们心愿对一个文章进行点赞统计,那么咱们依据文章 articleId 来生成 redisKey=
like_1121
,将 userId 作为 index - 首先是通过
getbit like_1121 userId
来判断是否点赞过,从而限度用户是否能够操作
Hash 以及 bitmap 的抉择和下面的考量范畴差不多
d. 布隆过滤器 bloomfilter
布隆过滤器堪称是赫赫有名了,咱们这里简略的介绍一下这货色是啥玩意
- 底层存储为一个 bitmap
- 当来一个数据时,通过 n 个 hash 函数,失去 n 个数值
- 将 hash 失去的 n 个数值,映射到 bitmap,标记对应的地位为 1
如果来一个数据,通过 hash 计算之后,若这个 n 个值,对应的 bitmap 都是 1,那么示意这个数据可能存在;如果有一个不为 1,则示意这个数据肯定不存在
请留神:不存在时,是肯定不存在;存在时,则不肯定
从下面的形容也晓得,bloomfilter 的底层数据结构就是 bitmap,当然它的关键点在 hash 算法;依据它未命中时肯定不存在的个性,十分实用于缓存击穿的问题解决
体验阐明
Redis 的布隆过滤器次要针对 >=4.0,通过插件的模式提供,我的项目源码地址为: https://github.com/RedisBloom/RedisBloom,上面依据 readme 的阐明,简略的体验一下 redis 中 bloomfilter 的应用姿态
# docker 形式装置
docker run -p 6379:6379 --name redis-redisbloom redislabs/rebloom:latest
# 通过 redis-cli 形式拜访
docker exec -it redis-redisbloom bash
# 开始应用
# redis-cli
127.0.0.1:6379> keys *
(empty array)
127.0.0.1:6379> bf.add newFilter hello
(integer) 1
127.0.0.1:6379> bf.exists newFilter hello
(integer) 1
127.0.0.1:6379> bf.exists newFilter hell
(integer) 0
bloomfilter 的应用比较简单,次要是两个命令 bf.add
增加元素,bf.exists
判断是否存在,请留神它没有删除哦
4. 小结
bitmap 位图属于一个比拟精美的数据结构,通常在数据量大的场景下,会有呈现的体现成果;redis 自身基于 String 数据结构来实现 bitmap 的性能反对,应用形式比较简单,基本上就上面三个命令
setbit key index 1/0
: 设置getbit key index
: 判断是否存在bitcount key
: 计数统计
本文也给出了 bitmap 的三个常见的利用场景
- 日活统计:次要借助
bitcount
来获取总数(前面会介绍,在日活十万百万以上时,应用 hyperLogLog 更优雅) - 点赞: 次要借助
setbit/getbit
来判断用户是否赞过,从而实现去重 - bloomfilter: 基于 bitmap 实现的布隆过滤器,宽泛用于去重的业务场景中(如缓存穿透,爬虫 url 去重等)
总的来讲,bitmap 属于易用,巧用的数据结构,用得好即能节俭内存也能够提高效率,用得不好貌似也不会带来太大的问题
II. 其余
0. 我的项目
系列博文
- 【DB 系列】Redis 之管道 Pipelined 应用姿态
- 【DB 系列】Redis 集群环境配置
- 【DB 系列】借助 Redis 搭建一个简略站点统计服务(利用篇)
- 【DB 系列】借助 Redis 实现排行榜性能(利用篇)
- 【DB 系列】Redis 之 ZSet 数据结构应用姿态
- 【DB 系列】Redis 之 Set 数据结构应用姿态
- 【DB 系列】Redis 之 Hash 数据结构应用姿态
- 【DB 系列】Redis 之 List 数据结构应用姿态
- 【DB 系列】Redis 之 String 数据结构的读写
- 【DB 系列】Redis 之 Jedis 配置
- 【DB 系列】Redis 之根本配置
工程源码
- 工程:https://github.com/liuyueyi/spring-boot-demo
- 我的项目源码: https://github.com/liuyueyi/spring-boot-demo/tree/master/spring-boot/122-redis-template
1. 一灰灰 Blog
尽信书则不如,以上内容,纯属一家之言,因集体能力无限,不免有疏漏和谬误之处,如发现 bug 或者有更好的倡议,欢送批评指正,不吝感谢
上面一灰灰的集体博客,记录所有学习和工作中的博文,欢送大家前去逛逛
- 一灰灰 Blog 集体博客 https://blog.hhui.top
- 一灰灰 Blog-Spring 专题博客 http://spring.hhui.top