bitmap原理和redis bitmap应用

摘要:
设计方案:使用位图是一种节省空间和高效的方法。只需要一个键,然后偏移用户id。如果联机,则将其设置为1,如果脱机,则将设置为0。3亿用户只需要36MB的空间。

bitmap原理

bitmap是什么?在计算机中一个字节(byte)=8位(bit),这里的bit就是位,数据的最小表示单位,map一般是表示地图或者映射。

简单回顾一下二进制的一些知识:

1byte=8bit

1个bit有二种状态:0或1

所以1个byte可以表示00000000->11111111,也就是十进制0-255

其中十进制和二进制的对应关系如下:

0 ---------> 0000 0000
 1 ---------> 0000 0001
 2 ---------> 0000 0010
 3 ---------> 0000 0011
 4 ---------> 0000 0100
 5 ---------> 0000 0101
 6 ---------> 0000 0110
 7 ---------> 0000 0111
 8 ---------> 0000 1000
 9 ---------> 0000 1001
10 ---------> 0000 1010
11 ---------> 0000 1011
12 ---------> 0000 1100
13 ---------> 0000 1101
14 ---------> 0000 1110
15 ---------> 0000 1111
.......................
.......................
255...........1111 1111

在大部分变成语言里,int类型一般的都是占4个byte,也就是32位,甭管你这个数字是1或者是21亿你都得栈32位,所以如果你现在有10亿数字需要存在内存里面,需要分配多少内存呢:

1000000000 * 4 / 1024 / 1024 = 3800MB,大概需要3800MB内存,这里计算出的数值只适合C,在PHP里面,一个整形变量占用的实际空间远远大于4byte,是好几倍。

为了解决这个问题,bitmap采用一种映射机制,举个例子,假如有1、3、7、2、5这5个数字需要存放,正常情况下需要5*4bye=20byte,但是bitmap只需要1byte,它是如何做到的?

假设下面是1byte,首先将所有位置为0:00000000

从第一个0开始数数,把对应数字的位置置位1,比如说第一个1那就是第2个位置置位1,第二个三就是把第4个位置置位1,以此类推

1 => 0 1 0 0 0 0 0 0
3 => 0 0 0 1 0 0 0 0
7 => 0 0 0 0 0 0 0 1
2 => 0 0 1 0 0 0 0 0
5 => 0 0 0 0 0 1 0 0

累加起来最终的串就是:0 1 1 1 0 1 0 1

其实最终的数字和二进制没什么关系,纯粹是数数,这个串就代表最大到7的数字,然后我们就开始数数,从0开始:

比如第1个位置是1,那就记个1
比如第2个位置是1,那就记个2
比如第3个位置是1,那就记个3
比如第5个位置是1,那就记个5
比如第7个位置是1,那就记个7

结果就是1 2 3 5 7,不仅仅排序,而且还排重了,如果按照这种转换机制,1个int类型,32位的话,可以表示0-31之间的数字

如果要表示最大一万的数,就需要1万个位的串,但是编程语言并没有这样的数据类型,但是可以用数组去模拟。举个例子,一个整形是32位,也就说我们大概需要314个数组元素来表示这个串:

  • 数组第1个元素 00 - 31
  • 数组第2个元素 32 - 63
  • 数组第3个元素 64 - 95
  • 数组第4个元素 96 - 127
  • ……

提到这个算法的好处,最大的好处就是节省内存,节省了好几十倍,适合处理大量数据,除了快速排序,还可以快速去重,快速查询是否存在,还有一个好听的应用Bloom Filter(布隆过滤器)

 redis bitmap应用

setBit

说明:给一个指定的key的值的第offset位赋值位value

参数:key offset value:bool or int (1 or 0)

返回值:long:0或1

 getBit

说明:返回一个指定key的二进制信息

参数:key offset

返回值:long

bitCount

说明:返回一个指定key中位的值为1的个数(是以byte为单位的bit)

参数:key start offset

返回值:long

bitOp

说明:对不同的二进制存储数据进行位运算(AND、OR、NOT、XOR)

参数:operation destkey key [key …]

 返回值:long

bitmap优势:

  1. 基于最小的单位bit进行存储,所以非常省空间
  2. 设置时候时间复杂度O(1),读取时候时间复杂度O(n),操作时非常快的
  3. 二进制数据的存储,进行相关计算的时候非常快
  4. 方便扩容

 bitmap限制:

redis中bit映射并限制在512MB之内,所以最大时2^32位。建议每个key的位数都控制下,因为读取时候时间复杂度O(n),越大的串读的时间花销越大。

bitmap使用场景:

1.视频属性的无限延伸:

需求分析:一个拥有亿级数据量的短视频app,视频存在各种属性(是否加锁、是否特效等等),需要做各种标记。

可能想到的解决方案:

  1. 存储在mysql中,肯定不行,一个是随着业务增长属性一直增加,并且存在有时间限制的属性,直接对数据库进行加减字段是非常不合理的做法。即使是存在一个字段中用json等压缩技术存储也存在读效率的问题,并且对于大几亿的数据来说,废弃的字段回收起来非常麻烦。
  2. 直接记录在redis中,根据业务属性+uid为key来存储。读写效率角度没毛病,但是存储的角度来说key的数据量都大于value了,太耗费空间了。即使是用json等压缩技术来存储。也存在问题,解压需要时间,并且大几亿的数据回收也是难题。

设计方案:

使用redis的bitmap进行存储。
key由属性id+视频分片id组成。value按照视频id对分片范围取模来决定偏移量offset。10亿视频一个属性约120m还是挺划算的。

伪代码:

bitmap原理和redis bitmap应用第1张

2.用户在线状态

 需求分析:需要对子项目提供一个接口,来提供某用户是否在线?

设计方案:使用bitmap是一个节约空间效率又高的一种方法,只需要一个key,然后用户id为偏移量offset,如果在线就设置为1,不在线就设置为0,3亿用户只需要36MB的空间。

伪代码:

bitmap原理和redis bitmap应用第2张

 3.统计活跃用户

需求分析:需要计算活跃用户的数据情况。

设计方案:使用时间作为缓存的key,然后用户id为offset,如果当日活跃过就设置为1。之后通过bitOp进行二进制计算算出在某段时间内用户的活跃情况。

伪代码:

bitmap原理和redis bitmap应用第3张

4.用户签到

需求分析:用户需要进行签到,对于签到的数据需要进行分析与相应的运运营策略。

设计方案:使用redis的bitmap,由于是长尾的记录,所以key主要由uid组成,设定一个初始时间,往后没加一天即对应value中的offset的位置。

伪代码:

bitmap原理和redis bitmap应用第4张

 原文出处:

https://blog.csdn.net/u011957758/article/details/74783347

https://zhuanlan.zhihu.com/p/67920410

免责声明:文章转载自《bitmap原理和redis bitmap应用》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇使用lambda调用有参函数mysql触发器下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

Redis Cluster 部署

内容: Redis 编译安装 Redis Cluster部署 Redis 集群扩容 Redis 指定机器下线  环境: 主机名 IP node1 192.168.10.1 node2 192.168.10.2 node3 192.168.10.3 node4 192.168.10.4 node5 192.168.10.5 node6 192....

接口幂等性思路

概念 接口幂等性就是用户对于统一操作发起的一次请求或多次请求的结果是一致的,不会因为多次点击而产生了副作用。 哪些场景需要保证接口的幂等性? 用户多次点击按钮。 用户页面回退再次提交 微服务之间相互调用,由于网络波动卡顿,导致feign触发重试机制。 其他情况... 天然幂等情况 以sql为例: 对于select * from table where...

redis队列的实现

redis中文官网:http://www.redis.cn/ 关于redis队列的实现方式有两种: 1、生产者消费者模式。 2、发布者订阅者模式。 详解: 1、生产者消费者模式。 普通版本: 比如一个队列里面,生产者A push了一个数据进去,消费者B pop 了这个数据,那个这个队列依旧为空。所以是一对一的。 至于是先进先出还是先进后出等,可以依照函数l...

Python中对文件的读写

读写文件前,我们先必须了解一下,在磁盘上读写文件的功能都是由操作系统提供的,现代操作系统不允许普通的程序直接操作磁盘。 读写文件就是请求操作系统打开一个文件对象(通常称为文件描述符),然后,通过操作系统提供的接口从这个文件对象中读取数据(读文件),或者把数据写入这个文件对象(写文件) open() 方法    完整的语法格式为: 1 open(file...

Mysql—二进制日志(binlog)

什么是二进制日志(binlog) binlog是记录所有数据库表结构变更(例如CREATE、ALTER TABLE…)以及表数据修改(INSERT、DELETE、UPDATE…)的二进制日志。多说一句,如果update操作没有造成数据变化,也是会记入binlog。 binlog不会记录SELECT和SHOW这类操作,因为这类操作对数据本身并没有修改,但你可...

Android 播放视频并获取指定时间的帧画面

最近做的项目要求既能播放视频(类似于视频播放器),又能每隔1s左右获取一帧视频画面,然后对图片进行处理,调查了一周,也被折磨了一周,总算找到了大致符合要求的方法。首先对调查过程中涉及到的方法进行简单介绍,再重点介绍最终所采用的方法,话不多说,进入正题。 一.MediaMetadataRetriever 播放视频并取得画面的一帧,大家最先想到应该都是这个,我...