基于Redis分布式BitMap的应用分析

目录
  • 一、序言
  • 二、BitMap结构
    • 1、内存消耗分析
    • 2、命令行操作BitMap
    • 3、客户端操作BitMap
    • 4、时间与空间复杂度
  • 三、BitMap应用
    • 1、回避缓存穿透
    • 2、与布隆过滤器的区别
  • 四、小结

一、序言

在实际开发中常常遇到如下需求:判断当前元素是否存在于已知的集合中,将已知集合中的元素维护一个HashSet,使用时只需耗时O(1)的时间复杂度便可判断出结果,Java内部或者Redis均提供相应的数据结构。使用此种方式除了占用内存空间外,几乎没有其它缺点。

当数据量达到亿级别时,内存空间的占用显著表现出来,BitMap便是解决此类问题的一种途径。

二、BitMap结构

1、内存消耗分析

Redis BitMap能够存储的数据范围为[0,2^32-1],超过Integer.MAX_VALUE上界值。

为了简化讨论,假设讨论的集合元素的范围为[0,Integer.MAX_VALUE],可以是其中的任何一个数。

使用HashSet数据结构占用内存空间仅与集合中的元素数量(N)相关。当集合中元素数量为N时,所需的内存空间大概为N*4/1024/1024MB,1亿条数据约占内存空间381MB

基于Redis的BitMap所占用的空间大小不与集合中元素数量相关,与集合中元素的最大值直接相关,因此BitMap所占用的内存空间范围为[N / 8 / 1024 / 1024,Integer.MAX_VALUE / 8 / 1024 / 1024]

// 测试1亿、5亿、10亿、Integer.MAX_VALUE
List<Integer> items = Arrays.asList(100000000, 500000000, 1000000000, Integer.MAX_VALUE);
for (Integer item : items) {
    int size = item / 8 / 1024 / 1024;
    System.out.printf("如果集合中最大值为%-10s,则所占用的内存空间为%3sMB%n",item, size);
}

这里给出了一组测试参考数据

如果集合中最大值为100000000 ,则所占用的内存空间为 11MB
如果集合中最大值为500000000 ,则所占用的内存空间为 59MB
如果集合中最大值为1000000000,则所占用的内存空间为119MB
如果集合中最大值为2147483647,则所占用的内存空间为255MB

当集合中数据增长到10亿条时,使用BItMap最大占用内存约为255MB,而使用HashSet增长到3.8GB

2、命令行操作BitMap

使用Redis命令行可直接操作BitMap,将offset位置的值标注为1,则表示当前数据存在。默认情况下未标注的位置值为0。

# 默认位不赋值为0,当数据存在于集合中,将对应位赋值为1
SETBIT key offset value
# 查看对应位数据是否存在(1表示存在,0表示不存在)
GETBIT key offset

3、客户端操作BitMap

这里提供一个SpringBoot生态的RedisUtils工具类,内部封装操作Redis BitMap的工具方法。

// 将当前位置标记为true
RedisUtils.setBit(BIT_MAP_KEY, orderId, true);
// 获取指定位置的值(对应数值是否存在)
RedisUtils.getBit(BIT_MAP_KEY, orderId)

上述工具类的依赖如下,如果找不到Jar包,请直接使用Maven原始仓库源,阿里云尚未同步完成。

<dependency>
    <groupId>xin.altitude.cms</groupId>
    <artifactId>ucode-cms-common</artifactId>
    <version>1.4.3</version>
</dependency>

4、时间与空间复杂度

BitMap的存储与取值时间复杂度为O(1),根据数值可直接映射下标。

BitMap占用内存空间复杂度为O(n),与集合中元素的最大值正相关,不是集合中元素的数量。

三、BitMap应用

1、回避缓存穿透

缓存穿透是指当前请求的数据在缓存中不存在,需要访问数据库获取数据(数据库中也不存在请求的数据)。缓存穿透给数据库带来了压力,恶意缓存穿透甚至能造成数据库宕机。

使用BitMap动态维护一个集合,当访问数据库前,先查询数据的主键是否存在集合中,以此作为是否访问数据库的依据。

BitMap新增数据或者移除数据属于轻量级操作,检查操作的准确度依赖于动态集合维护的闭环的完整性。比如向数据库增加数据时需要向BitMap中添加数据,从数据库中删除数据需要从BitMap中移除数据。如果要求严格的检查可靠性,则可以单独维护一个分布式定时任务,定期更新BitMap数据。

2、与布隆过滤器的区别

布隆过滤器与BitMap有相似的应用场景,但也有一定的区别。给定一个数,BitMap能准确知道是否存在于已知集合中;布隆过滤器能准确判断是否不在集合中,却不能肯定存在于集合中。

BitMap增加或者移除数据时间复杂度为O(1),方便快捷。布隆过滤器新建容易,剔除数据操作比较繁琐。

在一些需要精确判断的场景,优先选择BitMap,比如判断手机号是否已经注册。

四、小结

Redis BitMap不是一种新的数据结构,是利用字符串类型做的一层封装,看起来像一种新型数据结构。BitMap不像一种技术,更像是算法,在时间复杂度和空间复杂度之间寻找平衡点。

BitMap其它应用场景比如签到打卡,统计在线人数等等。

到此这篇关于基于Redis分布式BitMap的应用的文章就介绍到这了,更多相关Redis分布式BitMap内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • Redis中的bitmap详解

    1.什么是bitmap? bitmap也叫位图,也就是用一个bit位来表示一个东西的状态,我们都知道bit位是二进制,所以只有两种状态,0和1. 2.为什么要有bitmap? bitmap的出现就是为了大数据量而来的,但是前提是统计的这个大数据量每个的状态只能有两种,因为每一个bit位只能表示两种状态. 下面我们直接以一个统计亿级用户活动的状态来说明吧. 3.案例说明 3.1.案例描述 如果有一个上亿用户的系统,需要我们去统计每一天的用户登录情况,我们应该如何去解决? 前提条件:设置在9月19号

  • Redis分布式缓存:微信抢红包解决方案

    目录 一.场景分析 二.技术方案 三.案例实战 一.场景分析 微信抢红包已经在我们生活中很常见的场景了,特别是年底公司开年会和春节2个时间段,长辈领导都发红包,手都点抽筋了,也没抢到多少. 在这段时间里,对于单个群里的单个红包,qps也是上千的,对于整个微信红包系统,高峰的并发量是上亿的. 高峰的抢红包有3大特点: 包红包的人多:也就是创建红包的任务比较多,即红包系统是以单个红包的任务来区分,特点就是在高峰期红包任务多. 抢红包的人更多:当你发红包出去后,是几十甚至几百人来抢你的红包,即单红包的

  • 浅谈Redis位图(Bitmap)及Redis二进制中的问题

    Redis位图(Bitmap)及二进制的问题 SETBIT key offset value 对 key 所储存的字符串值,设置或清除指定偏移量上的位(bit).位的设置或清除取决于 value 参数,可以是 0 也可以是 1 .当 key 不存在时,自动生成一个新的字符串值.字符串会进行伸展(grown)以确保它可以将 value 保存在指定的偏移量上.当字符串值进行伸展时,空白位置以 0 填充.offset 参数必须大于或等于 0 ,小于 2^32 (bit 映射被限制在 512 MB 之内

  • Redis分布式锁详细介绍

    目录 分布式锁 redis实现分布式锁的原理 死锁问题 超时问题 锁误放问题 可重入性 Redlock 分布式锁 在单进程应用中,当一段代码同一时间内只能由一个线程执行时, 多线程下可能会出错,例如两个线程同时对一个数字做累加,两个线程同时拿到了该数字,例如40,一个线程加了10,一个线程加了20,正确结果应该是70, 但由于两个线程在自己的内存中一个算出的是50,一个算出的是60,此时二者都将自己的结果往该数字原本的地方写(保存), 这时候,肯定会有一个线程的值会被覆盖,因为读取->计算->

  • Redis基于Bitmap实现用户签到功能

    目录 功能分析 更多应用场景 总结 参考资料 很多应用上都有用户签到的功能,尤其是配合积分系统一起使用.现在有以下需求: 签到1天得1积分,连续签到2天得2积分,3天得3积分,3天以上均得3积分等. 如果连续签到中断,则重置计数,每月重置计数. 显示用户某月的签到次数和首次签到时间. 在日历控件上展示用户每月签到,可以切换年月显示. ... 功能分析 对于用户签到数据,如果直接采用数据库存储,当出现高并发访问时,对数据库压力会很大,例如双十一签到活动.这时候应该采用缓存,以减轻数据库的压力,Re

  • 基于Redis分布式BitMap的应用分析

    目录 一.序言 二.BitMap结构 1.内存消耗分析 2.命令行操作BitMap 3.客户端操作BitMap 4.时间与空间复杂度 三.BitMap应用 1.回避缓存穿透 2.与布隆过滤器的区别 四.小结 一.序言 在实际开发中常常遇到如下需求:判断当前元素是否存在于已知的集合中,将已知集合中的元素维护一个HashSet,使用时只需耗时O(1)的时间复杂度便可判断出结果,Java内部或者Redis均提供相应的数据结构.使用此种方式除了占用内存空间外,几乎没有其它缺点. 当数据量达到亿级别时,内

  • 基于redis key占用内存量分析

    Redis的指令看不出哪一类型的key,占用了多少内存,不好分析redis内存开销大的情况下,各应用程序使用缓存的占比. 借助第3方工具进行分析 1.采用2个工具结合 redis-rdb-tools+sqlite 2.sqlite linux服务器都会自带,安装redis-rdb-tools 使用pip安装 pip install redis-rdb-tools 源码安装 git clone https://github.com/sripathikrishnan/redis-rdb-tools

  • 基于Redis分布式锁Redisson及SpringBoot集成Redisson

    目录 - 分布式锁需要具备的条件和刚需 - Redisson使用 - SpringBoot集成Redisson - 分布式锁需要具备的条件和刚需 独占性:OnlyOne,任何时刻只能有且仅有一个线程持有 高可用:若redis集群环境下,不能因为某一个节点挂了而出现获取锁和释放锁失败的情况 防死锁:杜绝死锁,必须有超时控制机制或者撤销操作,有个兜底终止跳出方案 不乱抢:防止张冠李戴,不能私下unlock别人的锁,只能自己加锁自己释放 重入性:同一个节点的同一个线程如果获得锁之后,它也可以再次获取这

  • Go 语言下基于Redis分布式锁的实现方式

    分布式锁一般有三种实现方式:1. 数据库乐观锁:2. 基于Redis的分布式锁:3. 基于ZooKeeper的分布式锁.本篇博客将介绍第二种方式,基于Redis实现分布式锁.虽然网上已经有各种介绍Redis分布式锁实现的博客,然而他们的实现却有着各种各样的问题,为了避免误人子弟,本篇博客将详细介绍如何正确地实现Redis分布式锁. 项目地址: https://github.com/Spongecaptain/redisLock 1. Go 原生的互斥锁 Go 原生的互斥锁即 sync 包下的 M

  • 基于redis分布式锁实现秒杀功能

    最近在项目中遇到了类似"秒杀"的业务场景,在本篇博客中,我将用一个非常简单的demo,阐述实现所谓"秒杀"的基本思路. 业务场景 所谓秒杀,从业务角度看,是短时间内多个用户"争抢"资源,这里的资源在大部分秒杀场景里是商品:将业务抽象,技术角度看,秒杀就是多个线程对资源进行操作,所以实现秒杀,就必须控制线程对资源的争抢,既要保证高效并发,也要保证操作的正确. 一些可能的实现 刚才提到过,实现秒杀的关键点是控制线程对资源的争抢,根据基本的线程知识,可

  • 基于Redis分布式锁的实现代码

    概述 目前几乎很多大型网站及应用都是分布式部署的,分布式场景中的数据一致性问题一直是一个比较重要的话题.分布式的CAP理论告诉我们"任何一个分布式系统都无法同时满足一致性(Consistency).可用性(Availability)和分区容错性(Partition tolerance),最多只能同时满足两项."所以,很多系统在设计之初就要对这三者做出取舍.在互联网领域的绝大多数的场景中,都需要牺牲强一致性来换取系统的高可用性,系统往往只需要保证"最终一致性",只要这

  • 基于redis实现分布式锁的原理与方法

    前言 系统的不断扩大,分布式锁是最基本的保障.与单机的多线程不一样的是,分布式跨多个机器.线程的共享变量无法跨机器. 为了保证一个在高并发存场景下只能被同一个线程操作,java并发处理提供ReentrantLock或Synchronized进行互斥控制.但是这仅仅对单机环境有效.我们实现分布式锁大概通过三种方式. redis实现分布式锁 数据库实现分布式锁 zk实现分布式锁 今天我们介绍通过redis实现分布式锁.实际上这三种和java对比看属于一类.都是属于程序外部锁. 原理剖析 上述三种分布

  • Java基于redis实现分布式锁

    为了保证一个在高并发存场景下只能被同一个线程操作,java并发处理提供ReentrantLock或Synchronized进行互斥控制.但是这仅仅对单机环境有效.我们实现分布式锁大概通过三种方式. redis实现分布式锁 数据库实现分布式锁 zk实现分布式锁 实际上这三种和java对比看属于一类.都是属于程序外部锁. 原理剖析 上述三种分布式锁都是通过各自为依据对各个请求进行上锁,解锁从而控制放行还是拒绝.redis锁是基于其提供的setnx命令. setnx当且仅当key不存在.若给定key已

  • 详解基于redis实现分布式锁

    前言 为了保证一个在高并发存场景下只能被同一个线程操作,java并发处理提供ReentrantLock或Synchronized进行互斥控制.但是这仅仅对单机环境有效.我们实现分布式锁大概通过三种方式. redis实现分布式锁 数据库实现分布式锁 zk实现分布式锁 原理剖析 上述三种分布式锁都是通过各自为依据对各个请求进行上锁,解锁从而控制放行还是拒绝.redis锁是基于其提供的setnx命令. setnx当且仅当key不存在.若给定key已经存在,则setnx不做任何动作.setnx是一个原子

  • Springboot基于Redisson实现Redis分布式可重入锁源码解析

    目录 一.前言 二.为什么使用Redisson 1.我们打开官网 2.我们可以看到官方让我们去使用其他 3.打开官方推荐 4.找到文档 三.Springboot整合Redisson 1.导入依赖 2.以官网为例查看如何配置 3.编写配置类 4.官网测试加锁例子 5.根据官网简单Controller接口编写 6.测试 四.lock.lock()源码分析 1.打开RedissonLock实现类 2.找到实现方法 3.按住Ctrl进去lock方法 4.进去尝试获取锁方法 5.查看tryLockInne

随机推荐