zoukankan      html  css  js  c++  java
  • bitmap原理和redis bitmap应用

    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还是挺划算的。

    伪代码:

    2.用户在线状态

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

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

    伪代码:

     3.统计活跃用户

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

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

    伪代码:

    4.用户签到

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

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

    伪代码:

     原文出处:

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

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

  • 相关阅读:
    Tomcat配置远程调试端口(windows、Linux)
    Tomcat安装、配置和部署
    通过java.net.URLConnection发送HTTP请求(原生、爬虫)
    URL编码表、Base64编码表、HTTP消息含义
    机器学习重要的方法
    学习编程的方法
    对vi/vim的一些看法
    gvim安装中文文档
    vi常用命令
    内存对齐
  • 原文地址:https://www.cnblogs.com/shier-dong/p/15329872.html
Copyright © 2011-2022 走看看