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

本文涉及的产品
云数据库 Tair(兼容Redis),内存型 2GB
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 属于易用,巧用的数据结构,用得好即能节省内存也可以提高效率,用得不好貌似也不会带来太大的问题




相关实践学习
基于Redis实现在线游戏积分排行榜
本场景将介绍如何基于Redis数据库实现在线游戏中的游戏玩家积分排行榜功能。
云数据库 Redis 版使用教程
云数据库Redis版是兼容Redis协议标准的、提供持久化的内存数据库服务,基于高可靠双机热备架构及可无缝扩展的集群架构,满足高读写性能场景及容量需弹性变配的业务需求。 产品详情:https://www.aliyun.com/product/kvstore &nbsp; &nbsp; ------------------------------------------------------------------------- 阿里云数据库体验:数据库上云实战 开发者云会免费提供一台带自建MySQL的源数据库&nbsp;ECS 实例和一台目标数据库&nbsp;RDS实例。跟着指引,您可以一步步实现将ECS自建数据库迁移到目标数据库RDS。 点击下方链接,领取免费ECS&amp;RDS资源,30分钟完成数据库上云实战!https://developer.aliyun.com/adc/scenario/51eefbd1894e42f6bb9acacadd3f9121?spm=a2c6h.13788135.J_3257954370.9.4ba85f24utseFl
目录
打赏
0
0
0
0
13
分享
相关文章
Redis应用—7.大Value处理方案
本文介绍了一种用于监控Redis大key的方案设计及其实现步骤。主要内容包括:方案设计、安装与配置环境、binlog数据消费者。
141 29
Redis应用—7.大Value处理方案
Redis应用—6.热key探测设计与实践
热key问题在高并发系统中可能导致数据层和服务层的严重瓶颈,如Redis集群瘫痪和用户体验下降。为解决此问题,京东开发了JdHotkey热key探测框架,具备实时性、准确性、集群一致性和高性能等特点。该框架由etcd集群、Client端jar包、Worker端集群和Dashboard控制台组成,通过分布式计算快速识别热key并推送至应用内存,有效减轻数据层负载,提升服务性能。JdHotkey适用于多种场景,安装部署简便,支持毫秒级热key探测和集群一致性维护。
151 61
Redis应用—6.热key探测设计与实践
Redis应用—8.相关的缓存框架
本文介绍了Ehcache和Guava Cache两个缓存框架及其使用方法,以及如何自定义缓存。主要内容包括:Ehcache缓存框架、Guava Cache缓存框架、自定义缓存。总结:Ehcache适合用作本地缓存或与Redis结合使用,Guava Cache则提供了更灵活的缓存管理和更高的并发性能。自定义缓存可以根据具体需求选择不同的数据结构和引用类型来实现特定的缓存策略。
126 16
Redis应用—8.相关的缓存框架
Redis游戏积分排行榜项目中通义灵码的应用实战
Redis游戏积分排行榜项目中通义灵码的应用实战
146 4
场景题:百万数据插入Redis有哪些实现方案?
场景题:百万数据插入Redis有哪些实现方案?
90 1
场景题:百万数据插入Redis有哪些实现方案?
Redis应用—5.Redis相关解决方案
本文介绍了Redis在实际应用中遇到的几个关键问题及其解决方案,包括:数据库与缓存一致性方案、热key探测系统处理热key问题、缓存大value监控和切分处理方案、Redis内存不足强制回收监控告警方案、Redis集群缓存雪崩自动探测 + 限流降级方案、缓存击穿的解决方法。
Redis应用—5.Redis相关解决方案
Redis应用—4.在库存里的应用
本文介绍了社区电商系统库存模块的设计与实现,涵盖以下关键点:库存模块设计、库存缓存分片和渐进式同步方案、下单库存扣减方案、商品库存设置流程与异步落库、库存扣减逻辑、库存查询,这些设计确保了库存管理在高并发场景下的高效性和数据一致性。
Redis应用—4.在库存里的应用
Redis应用—2.在列表数据里的应用
本文介绍了基于数据库和缓存双写的分享贴功能设计,包括:基于数据库 + 缓存双写的分享贴功能、查询分享贴列表缓存时的延迟构建、分页列表惰性缓存方案、用户分享贴列表数据按页缓存实现精准过期控制、用户分享贴列表的分页缓存异步更新、数据库与缓存的分页数据一致性方案、热门用户分享贴列表的分页缓存失效时消除并发线程串行等待锁的影响。总结:该设计通过合理的缓存策略和异步处理机制,有效提升了系统性能,降低了内存占用,并确保了数据的一致性和高可用性。
Redis应用—2.在列表数据里的应用
Redis应用—9.简单应用汇总
本文主要介绍了Redis的一些简单应用。
153 24
Redis应用—3.在购物车里的应用
本文详细介绍了社区电商购物车的设计与实现,涵盖多个关键方面:读多写多场景分析、复杂缓存与异步落库、异步落库问题处理、阈值检查与重复加入逻辑、多线程并发问题解决、查询更新功能、选中提交功能。

热门文章

最新文章