【SpringBoot DB 系列】Redis 高级特性之 Bitmap 使用姿势及应用场景介绍

本文涉及的产品
Redis 开源版,标准版 2GB
推荐场景:
搭建游戏排行榜
简介: 前面介绍过 redis 的五种基本数据结构,如 String,List, Set, ZSet, Hash,这些属于相对常见了;在这些基本结果之上,redis 还提供了一些更高级的功能,如 geo, bitmap, hyperloglog,pub/sub,本文将主要介绍 Bitmap 的使用姿势以及其适用场景,主要知识点包括

网络异常,图片无法展示
|


前面介绍过 redis 的五种基本数据结构,如 String,List, Set, ZSet, Hash,这些属于相对常见了;在这些基本结果之上,redis 还提供了一些更高级的功能,如 geo, bitmap, hyperloglog,pub/sub,本文将主要介绍 Bitmap 的使用姿势以及其适用场景,主要知识点包括


  • bitmap 基本使用
  • 日活统计应用场景中 bitmap 使用姿势
  • 点赞去重应用场景中 bitmap 使用姿势
  • 布隆过滤器 bloomfilter 基本原理及体验 case


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 主要就三个操作命令,setbitgetbit以及 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,通过插件的形式提供,项目源码地址为: github.com/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 属于易用,巧用的数据结构,用得好即能节省内存也可以提高效率,用得不好貌似也不会带来太大的问题




相关文章
|
29天前
|
NoSQL Java 网络安全
SpringBoot启动时连接Redis报错:ERR This instance has cluster support disabled - 如何解决?
通过以上步骤一般可以解决由于配置不匹配造成的连接错误。在调试问题时,一定要确保服务端和客户端的Redis配置保持同步一致。这能够确保SpringBoot应用顺利连接到正确配置的Redis服务,无论是单机模式还是集群模式。
188 5
|
2月前
|
缓存 Java 应用服务中间件
Spring Boot配置优化:Tomcat+数据库+缓存+日志,全场景教程
本文详解Spring Boot十大核心配置优化技巧,涵盖Tomcat连接池、数据库连接池、Jackson时区、日志管理、缓存策略、异步线程池等关键配置,结合代码示例与通俗解释,助你轻松掌握高并发场景下的性能调优方法,适用于实际项目落地。
517 5
|
2月前
|
NoSQL Java 调度
分布式锁与分布式锁使用 Redis 和 Spring Boot 进行调度锁(不带 ShedLock)
分布式锁是分布式系统中用于同步多节点访问共享资源的机制,防止并发操作带来的冲突。本文介绍了基于Spring Boot和Redis实现分布式锁的技术方案,涵盖锁的获取与释放、Redis配置、服务调度及多实例运行等内容,通过Docker Compose搭建环境,验证了锁的有效性与互斥特性。
201 0
分布式锁与分布式锁使用 Redis 和 Spring Boot 进行调度锁(不带 ShedLock)
|
2月前
|
NoSQL 数据可视化 Redis
redis上db复制的方法
首先排除使用命令行实现,因为没有现成的命令可以完成db复制,跨redis实例的复制迁移就更加没有这种命令了。假如非要使用命令来实现,要写大量的脚本,但是这样可靠性和速度无法保证,因为你无法保证你写的程序是否会有bug。db的复制,可以使用yunedit-redis来实现,yunedit-redis有可视化界面,复制起来非常简单。
|
8月前
|
NoSQL Java 关系型数据库
微服务——SpringBoot使用归纳——Spring Boot 中集成Redis——Redis 介绍
本文介绍在 Spring Boot 中集成 Redis 的方法。Redis 是一种支持多种数据结构的非关系型数据库(NoSQL),具备高并发、高性能和灵活扩展的特点,适用于缓存、实时数据分析等场景。其数据以键值对形式存储,支持字符串、哈希、列表、集合等类型。通过将 Redis 与 Mysql 集群结合使用,可实现数据同步,提升系统稳定性。例如,在网站架构中优先从 Redis 获取数据,故障时回退至 Mysql,确保服务不中断。
336 0
微服务——SpringBoot使用归纳——Spring Boot 中集成Redis——Redis 介绍
|
8月前
|
缓存 NoSQL Java
基于SpringBoot的Redis开发实战教程
Redis在Spring Boot中的应用非常广泛,其高性能和灵活性使其成为构建高效分布式系统的理想选择。通过深入理解本文的内容,您可以更好地利用Redis的特性,为应用程序提供高效的缓存和消息处理能力。
745 79
|
9月前
|
NoSQL Java Redis
Springboot使用Redis实现分布式锁
通过这些步骤和示例,您可以系统地了解如何在Spring Boot中使用Redis实现分布式锁,并在实际项目中应用。希望这些内容对您的学习和工作有所帮助。
892 83
|
6月前
|
消息中间件 NoSQL Unix
Redis的基本特性以及其基础命令用法
这只是冰山一角,Redis的强大功能和简洁的操作方法值得我们深入了解和掌握,是复杂数据问题解决的有力工具。所以,来一场有趣的Redis冒险吧!
191 6
|
5月前
|
机器学习/深度学习 数据采集 人机交互
springboot+redis互联网医院智能导诊系统源码,基于医疗大模型、知识图谱、人机交互方式实现
智能导诊系统基于医疗大模型、知识图谱与人机交互技术,解决患者“知症不知病”“挂错号”等问题。通过多模态交互(语音、文字、图片等)收集病情信息,结合医学知识图谱和深度推理,实现精准的科室推荐和分级诊疗引导。系统支持基于规则模板和数据模型两种开发原理:前者依赖人工设定症状-科室规则,后者通过机器学习或深度学习分析问诊数据。其特点包括快速病情收集、智能病症关联推理、最佳就医推荐、分级导流以及与院内平台联动,提升患者就诊效率和服务体验。技术架构采用 SpringBoot+Redis+MyBatis Plus+MySQL+RocketMQ,确保高效稳定运行。
412 0
|
8月前
|
存储 人工智能 NoSQL
SpringBoot整合Redis、ApacheSolr和SpringSession
本文介绍了如何使用SpringBoot整合Redis、ApacheSolr和SpringSession。SpringBoot以其便捷的配置方式受到开发者青睐,通过引入对应的starter依赖,可轻松实现功能整合。对于Redis,可通过配置RedisSentinel实现高可用;SpringSession则提供集群Session管理,支持多种存储方式如Redis;整合ApacheSolr时,借助Zookeeper搭建SolrCloud提高可用性。文中详细说明了各组件的配置步骤与代码示例,方便开发者快速上手。
158 11