返回顶部
首页 > 资讯 > 数据库 >redis的bitmap使用实例分析
  • 548
分享到

redis的bitmap使用实例分析

2024-04-02 19:04:59 548人浏览 独家记忆
摘要

这篇文章主要讲解了“Redis的bitmap使用实例分析”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“redis的bitmap使用实例分析”吧!1.位图简介

这篇文章主要讲解了“Redis的bitmap使用实例分析”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“redis的bitmap使用实例分析”吧!

redis的bitmap使用实例分析

1.位图简介

如果我们需要记录某一用户在一年中每天是否有登录我们的系统这一需求该如何完成呢?如果使用KV存储,每个用户需要记录365个,当用户量上亿时,这所需要的存储空间是惊人的。

Redis 为我们提供了位图这一数据结构,每个用户每天的登录记录只占据一位,365天就是365位,仅仅需要46字节就可存储,极大地节约了存储空间。

redis的bitmap使用实例分析

位图数据结构其实并不是一个全新的玩意,我们可以简单的认为就是个数组,只是里面的内容只能为0或1而已(二进制位数组)。

2.命令实战

Redis提供了SETBITGETBITBITCOUNTBITOP四个常用命令用于处理二进制位数组

  • SETBIT:为位数组指定偏移量上的二进制位设置值,偏移量从0开始计数,二进制位的值只能为0或1。返回原位置值。

  • GETBIT:获取指定偏移量上二进制位的值。

  • BITCOUNT:统计位数组中值为1的二进制位数量。

  • BITOP:对多个位数组进行按位与、或、异或运算。

127.0.0.1:6379> SETBIT first 0 1    # 0000 0001
(integer) 0
127.0.0.1:6379> SETBIT first 3 1    # 0000 1001
(integer) 0
127.0.0.1:6379> SETBIT first 0 0    # 0000 1000
(integer) 1

127.0.0.1:6379> GETBIT first 0
(integer) 0
127.0.0.1:6379> GETBIT first 3
(integer) 1

127.0.0.1:6379> BITCOUNT first      # 0000 1000
(integer) 1
127.0.0.1:6379> SETBIT first 0 1    # 0000 1001
(integer) 0
127.0.0.1:6379> BITCOUNT first      # 0000 1001
(integer) 2
127.0.0.1:6379> SETBIT first 1 1    # 0000 1011
(integer) 0
127.0.0.1:6379> BITCOUNT first      # 0000 1011
(integer) 3

127.0.0.1:6379> SETBIT x 3 1        
(integer) 0
127.0.0.1:6379> SETBIT x 1 1        
(integer) 0
127.0.0.1:6379> SETBIT x 0 1        # 0000 1011
(integer) 0
127.0.0.1:6379> SETBIT y 2 1        
(integer) 0
127.0.0.1:6379> SETBIT y 1 1        # 0000 0110
(integer) 0
127.0.0.1:6379> SETBIT z 2 1        
(integer) 0
127.0.0.1:6379> SETBIT z 0 1        # 0000 0101
(integer) 0

127.0.0.1:6379> BITOP AND andRes x y z    #0000 0000
(integer) 1
127.0.0.1:6379> BITOP OR orRes x y z      #0000 1111
(integer) 1
127.0.0.1:6379> BITOP XOR x y z           #0000 1000
(integer) 1

# 对给定的位数组进行按位取反
127.0.0.1:6379> SETBIT value 0 1
(integer) 0
127.0.0.1:6379> SETBIT value 3 1            #0000 1001
(integer) 0
127.0.0.1:6379> BITOP NOT notValue value    #1111 0110
(integer) 1

3.BitMap源码分析

3.1 数据结构

如下展示了一个用 SDS 表示的一字节(8位)长的位图:

redis的bitmap使用实例分析

扩展:Redis 中的每个对象都是有一个 redisObject 结构表示的。

typedef struct redisObject {
// 类型
unsigned type:4;
// 编码
unsigned encoding:4;
unsigned lru:REDIS_LRU_BITS; 
// 引用计数
int refcount;
// 执行底层实现的数据结构的指针
void *ptr;
} robj;
  • type 的值为 REDIS_STRING表示这是一个字符串对象

  • sdshdr.len 的值为1表示这个SDS保存了一个1字节大小的位数组

  • buf数组中的buf[0]实际保存了位数组

  • buf数组中的buf[1]为自动追加的\0字符

为了便于我们观察,buf数组的每个字节都用一行来表示,buf[i]表示这是buf数组的第i个字节,buf[i]之后的8个格子表示这个字节上的8位。

为再次拉齐各位思想,如下展示了另外一个位数组:

redis的bitmap使用实例分析

位数组由buf[0]、buf[1]和buf[2]三个字节保存,真实数据为1111 0000 1100 0011 1010 0101

3.2 GETBIT

GETBIT用于返回位数组在偏移量上的二进制位的值。值得我们注意的是,GETBIT的时间复杂度是O(1)

GETBIT命令的执行过程如下:

  1. 计算$ byte = \lfloor offset\p8 \rfloor $ (即>>3),byte 值表示指定的                             o                         f                         f                         s                         e                         t                            offset                 offset 位于位数组的哪个字节(计算在第几行);

  2. 指定                             b                         u                         f                         [                         i                         ]                            buf[i]                 buf[i] 中的                             i                            i                 i了,接下来就要计算在8个字节中的第几位呢?使用 $ bit = (offset\ %\ 8)+1 $计算可得;

  3. 根据                             b                         y                         t                         e                            byte                 byte 和                             b                         i                         t                            bit                 bit 在位数组中定位到目标值返回即可。

GETBIT命令源码如下所示:

void getbitCommand(client *c) {
    robj *o;
    char llbuf[32];
    uint64_t bitoffset;
    size_t byte, bit;
    size_t bitval = 0;
    // 获取offset
    if (getBitOffsetFromArgument(c,c->argv[2],&bitoffset,0,0) != C_OK)
        return;
    // 查找对应的位图对象
    if ((o = lookupKeyReadOrReply(c,c->argv[1],shared.czero)) == NULL ||
        checkType(c,o,OBJ_STRING)) return;
		// 计算offset位于位数组的哪一行
    byte = bitoffset >> 3;
    // 计算offset在一行中的第几位,等同于取模
    bit = 7 - (bitoffset & 0x7);
    // #define sdsEncodedObject(objptr) (objptr->encoding == OBJ_ENCODING_RAW || objptr->encoding == OBJ_ENCODING_EMBSTR)
    if (sdsEncodedObject(o)) {
        // SDS 是RAW 或者 EMBSTR类型
        if (byte < sdslen(o->ptr))
            // 获取指定位置的值
            // 注意它不是真正的一个二维数组不能用((uint8_t*)o->ptr)[byte][bit]去获取呀~
            bitval = ((uint8_t*)o->ptr)[byte] & (1 << bit);
    } else {
        //  SDS 是 REDIS_ENCODING_INT 类型的整数,先转为String
        if (byte < (size_t)ll2string(llbuf,sizeof(llbuf),(long)o->ptr))
            bitval = llbuf[byte] & (1 << bit);
    }

    addReply(c, bitval ? shared.cone : shared.czero);
}

redis的bitmap使用实例分析

举个栗子

GETBIT array 3为例,array表示上图中三个字节的位数组。

1. $byte = \lfloor 3 \p8 \rfloor$ 得到值为0,说明在 $buf[0]$ 上
2. $bit = (3\ mod\ 8 ) + 1$得到值为4
3. 定位到 $buf[0]$ 字节的从左至右第4个位置上

redis的bitmap使用实例分析

因为 GETBIT 命令执行的所有操作都可以在常数时间内完成,所以该命令的算法复杂度为O(1)。

3.3 SETBIT

SETBIT用于将位数组在偏移量的二进制位的值设为value,并向客户端返回旧值。

SITBIT命令的执行过程如下:

  1. 计算                             l                         e                         n                         =                         ⌊                         o                         f                         f                         s                         e                         t                         ÷                         8                         ⌋                         +                         1                            len = \lfloor offset÷8\rfloor + 1                 len=⌊offset÷8⌋+1,                             l                         e                         n                            len                 len值记录了保存                             o                         f                         f                         s                         e                         t                            offset                 offset偏移量指定的二进制位至少需要多少字节

  2. 检查位数组的长度是否小于                             l                         e                         n                            len                 len,如果是的话,将SDS的长度扩展为len字节,并将所有新扩展空间的二进制位设置为0

  3. 计算                             b                         y                         t                         e                         =                         ⌊                         o                         f                         f                         s                         e                         t                         ÷                         8                         ⌋                            byte = \lfloor offset÷8\rfloor                 byte=⌊offset÷8⌋,                             b                         y                         t                         e                            byte                 byte值表示指定的                             o                         f                         f                         s                         e                         t                            offset                 offset位于位数组的那个字节(就是计算在那个                             b                         u                         f                         [                         i                         ]                            buf[i]                 buf[i]中的                             i                            i                 i)

  4. 使用                             b                         i                         t                         =                         (                         o                         f                         f                         s                         e                         t                                                   m                         o                         d                                                   8                         )                         +                         1                            bit = (offset\ mod\ 8)+1                 bit=(offset mod 8)+1计算可得目标                             b                         u                         f                         [                         i                         ]                            buf[i]                 buf[i]的具体第几位

  5. 根据                             b                         y                         t                         e                            byte                 byte和                             b                         i                         t                            bit                 bit的值,首先保存                             o                         l                         d                         V                         a                         l                         u                         e                            oldValue                 oldValue,然后将新值                             v                         a                         l                         u                         e                            value                 value设置到目标位上

  6. 返回旧值

因为SETBIT命令执行的所有操作都可以在常数时间内完成,所以该命令的算法复杂度为O(1)。

SETBIT命令源码如下所示:

void setbitCommand(client *c) {
    robj *o;
    char *err = "bit is not an integer or out of range";
    uint64_t bitoffset;
    ssize_t byte, bit;
    int byteval, bitval;
    long on;
    // 获取offset
    if (getBitOffsetFromArgument(c,c->argv[2],&bitoffset,0,0) != C_OK)
        return;
    // 获取我们需要设置的值
    if (getLongFromObjectOrReply(c,c->argv[3],&on,err) != C_OK)
        return;

    
    if (on & ~1) {
        // 设置了0和1之外的值,直接报错
        addReplyError(c,err);
        return;
    }
    // 根据key查询SDS对象(会自动扩容)
    if ((o = lookupStringForBitCommand(c,bitoffset)) == NULL) return;

    
    byte = bitoffset >> 3;
    byteval = ((uint8_t*)o->ptr)[byte];
    bit = 7 - (bitoffset & 0x7);
    bitval = byteval & (1 << bit);

    
    byteval &= ~(1 << bit);
    byteval |= ((on & 0x1) << bit);
    ((uint8_t*)o->ptr)[byte] = byteval;
    // 发送数据修改通知
    signalModifiedKey(c,c->db,c->argv[1]);
    notifyKeyspaceEvent(NOTIFY_STRING,"setbit",c->argv[1],c->db->id);
    server.dirty++;
    addReply(c, bitval ? shared.cone : shared.czero);
}

redis的bitmap使用实例分析

举个栗子1.0

array表示上图中的三个字节位数组。以SETBIT array 10 1为例:

  1. $ len = \lfloor10÷8\rfloor + 1$得到值为2,说明至少需要2字节长的位数组

  2. 检查是否需要扩容,不需要

  3. 计算$byte = 1                             ,                         计                         算                            ,计算                 ,计算bit = 3                             ,                         保                         存                            ,保存                 ,保存oldValue$,设置新值

  4. 返回                             o                         l                         d                         V                         a                         l                         u                         e                            oldValue                 oldValue

redis的bitmap使用实例分析

举个栗子2.0

假设目标位数组为1字节长度。执行SETBIT array 12 1,执行如下:

  1.                             l                         e                         n                         =                         ⌊                         12                         ÷                         8                         ⌋                         +                         1                            len = ⌊12÷8⌋ + 1                 len=⌊12÷8⌋+1 得到值为2,说明需要2字节长的 SDS

  2. 检查是否需要扩容,需要呀!由 SDS 的自动扩容机制可知, SDS 将按新长度的两倍扩容。

  3. 计算 $byte = 1 $

  4. 计算 $bit = 5 $

  5. 保存                             o                         l                         d                         V                         a                         l                         u                         e                            oldValue                 oldValue,设置新值

  6. 返回                             o                         l                         d                         V                         a                         l                         u                         e                            oldValue                 oldValue

redis的bitmap使用实例分析

3.4 BITCOUNT

BITCOUNT命令用于统计给定位数组中值为1的二进制位的数量。功能似乎不复杂,但实际上要高效地实现这个命令并不容易,需要用到一些精巧的算法

统计一个位数组中非0二进制位的数量在数学上被称为"计算汉明重量"。

3.4.1 暴力遍历

实现BITCOUNT命令最简单直接的方法,就是遍历位数组中的每个二进制位,并在遇到值为1的二进制位时将计数器加1。

小数据量还好,大数据量直接PASS!

3.4.2 查表法

对于一个有限集合来说,集合元素的排列方式是有限的,并且对于一个有限长度的位数组来说,它能表示的二进制位排列也是有限的。根据这个原理,我们可以创建一个表,表的键为某种排列的位数组,而表的值则是相应位数组中值为1的二进制位的数量。

对于8位长的位数组来说,我们可以创建下表,通过这个表格我们可以一次从位数组中读入8位,然后根据这8位的值进行查表,直接知道这个值包含了多少个1。

redis的bitmap使用实例分析

可惜,查表法耗内存呀!

3.4.3 二进制位统计算法:variable-precision SWAR

目前已知效率最好的通用算法为variable-precision SWAR算法,该算法通过一系列位移和位运算操作,可以在常数时间(这就很牛逼了)内计算多个字节的汉明重量,并且不需要使用任何额外的内存。

SWAR算法代码如下所示:

uint32_t swar(uint32_t i) {
    // 5的二进制:0101
    i = (i & 0x55555555) + ((i >> 1) & 0x55555555);
    // 3的二进制:0011
    i = (i & 0x33333333) + ((i >> 2) & 0x33333333);
    i = (i & 0x0F0F0F0F) + ((i >> 4) & 0x0F0F0F0F);
    i = (i*(0x01010101) >> 24);
    return i;
  
    i = i - ((i >> 1) & 0x55555555);
    i = (i & 0x33333333) + ((i >> 2) & 0x33333333);
    return (((i + (i >> 4)) & 0x0F0F0F0F) * 0x01010101) >> 24;
}

下面描述一下这几步都干了啥:

  1. 步骤一计算出的值i的二进制表示可以按每两个二进制位为一组进行分组,各组的十进制表示就是该组的1的数量;

  2. 步骤二计算出的值i的二进制表示可以按每四个二进制位为一组进行分组,各组的十进制表示就是该组的1的数量;

  3. 步骤三计算出的值i的二进制表示可以按每八个二进制位为一组进行分组,各组的十进制表示就是该组的1的数量;

  4. 步骤四的i*0x01010101语句计算出bitarray中1的数量并记录在二进制位的最高八位,而>>24语句则通过右移运算,将bitarray的汉明重量移动到最低八位,得出的结果就是bitarray的汉明重量。

举个栗子

对于调用swar(0xFBB4080B),步骤一将计算出值0xA6640406,这个值表的每两个二进制位的十进制表示记录了0xFBB4080B每两个二进制位的汉明重量。

redis的bitmap使用实例分析

步骤二将计算出值0x43310103,这个值表的每四个二进制位的十进制表示记录了0xFBB4080B每四个二进制位的汉明重量。

redis的bitmap使用实例分析

步骤三将计算出值0x7040103,这个值表的每八个二进制位的十进制表示记录了0xFBB4080B每八个二进制位的汉明重量。

redis的bitmap使用实例分析

步骤四首先计算0x7040103 * 0x01010101 = 0xF080403,将汉明重量聚集到二进制位的最高八位。

redis的bitmap使用实例分析

之后计算0xF080403 >> 24,将汉明重量移动到低八位,得到最终值0x1111,即十进制15。

如果您是Java程序员,可以去看看Integer.bitCount方法,也是基于SWAR算法的思想哦!

大家也可以看看StackFlow上大神对它的讲解:[How does this alGorithm to count the number of set bits in a 32-bit integer work?](https://stackoverflow.com/questions/22081738/how-does-this-algorithm-to-count-the-number-of-set-bits-in-a-32-bit-integer-work)3.4.4 源码分析

Redis 中通过调用redisPopcount方法统计汉明重量,源码如下所示:

long long redisPopcount(void *s, long count) {
    long long bits = 0;
    unsigned char *p = s;
    uint32_t *p4;
    // 为查表法准备的表
    static const unsigned char bitsinbyte[256] = {0,1,1,2,1,2,2,3,1,2,2,3,2,3,3,4,1,2,2,3,2,3,3,4,2,3,3,4,3,4,4,5,1,2,2,3,2,3,3,4,2,3,3,4,3,4,4,5,2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6,1,2,2,3,2,3,3,4,2,3,3,4,3,4,4,5,2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6,2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6,3,4,4,5,4,5,5,6,4,5,5,6,5,6,6,7,1,2,2,3,2,3,3,4,2,3,3,4,3,4,4,5,2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6,2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6,3,4,4,5,4,5,5,6,4,5,5,6,5,6,6,7,2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6,3,4,4,5,4,5,5,6,4,5,5,6,5,6,6,7,3,4,4,5,4,5,5,6,4,5,5,6,5,6,6,7,4,5,5,6,5,6,6,7,5,6,6,7,6,7,7,8};
    // CPU一次性读取8个字节,如果4字节跨了两个8字节,需要读取两次才行
    // 所以考虑4字节对齐,只需读取一次就可以读取完毕
    while((unsigned long)p & 3 && count) {
        bits += bitsinbyte[*p++];
        count--;
    }

    // 一次性处理28字节,单独看一个aux就容易理解了,其实就是SWAR算法
    // uint32_t:4字节
    p4 = (uint32_t*)p;
    while(count>=28) {
        uint32_t aux1, aux2, aux3, aux4, aux5, aux6, aux7;

        aux1 = *p4++;// 一次性读取4字节
        aux2 = *p4++;
        aux3 = *p4++;
        aux4 = *p4++;
        aux5 = *p4++;
        aux6 = *p4++;
        aux7 = *p4++;
        count -= 28;// 共处理了4*7=28个字节,所以count需要减去28

        aux1 = aux1 - ((aux1 >> 1) & 0x55555555);
        aux1 = (aux1 & 0x33333333) + ((aux1 >> 2) & 0x33333333);
        aux2 = aux2 - ((aux2 >> 1) & 0x55555555);
        aux2 = (aux2 & 0x33333333) + ((aux2 >> 2) & 0x33333333);
        aux3 = aux3 - ((aux3 >> 1) & 0x55555555);
        aux3 = (aux3 & 0x33333333) + ((aux3 >> 2) & 0x33333333);
        aux4 = aux4 - ((aux4 >> 1) & 0x55555555);
        aux4 = (aux4 & 0x33333333) + ((aux4 >> 2) & 0x33333333);
        aux5 = aux5 - ((aux5 >> 1) & 0x55555555);
        aux5 = (aux5 & 0x33333333) + ((aux5 >> 2) & 0x33333333);
        aux6 = aux6 - ((aux6 >> 1) & 0x55555555);
        aux6 = (aux6 & 0x33333333) + ((aux6 >> 2) & 0x33333333);
        aux7 = aux7 - ((aux7 >> 1) & 0x55555555);
        aux7 = (aux7 & 0x33333333) + ((aux7 >> 2) & 0x33333333);
        bits += ((((aux1 + (aux1 >> 4)) & 0x0F0F0F0F) +
                    ((aux2 + (aux2 >> 4)) & 0x0F0F0F0F) +
                    ((aux3 + (aux3 >> 4)) & 0x0F0F0F0F) +
                    ((aux4 + (aux4 >> 4)) & 0x0F0F0F0F) +
                    ((aux5 + (aux5 >> 4)) & 0x0F0F0F0F) +
                    ((aux6 + (aux6 >> 4)) & 0x0F0F0F0F) +
                    ((aux7 + (aux7 >> 4)) & 0x0F0F0F0F))* 0x01010101) >> 24;
    }
    
    p = (unsigned char*)p4;
    while(count--) bits += bitsinbyte[*p++];
    return bits;
}

不难发现 Redis 中同时运用了查表法SWAR算法完成BITCOUNT功能。

4.面试题:40亿QQ号去重

如果没有1GB的内存限制,我们可以使用排序和Set完成这个算法:

  • 排序:① 首先将40亿个QQ号进行排序;② 从小到大遍历,跳过重复元素只取第一个元素。

  • Set:将40亿个QQ号统统放进Set集合中,自动完成去重,Perfect

这样回答是要GG的节奏呀!

对40亿个QQ号进行排序需要多少时间?这个存储了40亿QQ号的数组容量已经超过1GB了,同理Set集合存储这么多的QQ号也导致内存超限了。

BitMap去重

**这不巧了么~我们可以使用刚刚学的BITMAP来去重呀!**一个字节可以记录8个数是否存在(类似于计数排序),将QQ号对应的offset的值设置为1表示此数存在,遍历完40亿个QQ号后直接统计BITMAP上值为1的offset即可完成QQ号的去重。

如果是对40亿个QQ号进行排序也是可以用位图完成的哦~一样的思想

5.位图实战

既然我们深入了解了BITMAP,那不进行个实战项目可说不过去呀!

我们使用BITMAP实现GITHUB中统计每天提交次数的这个小功能,基于SpringBoot+Echarts实现

redis的bitmap使用实例分析

如果是记录登录状态我们可以很方便的使用0和1记录,如果是记录提交次数就显得BITMAP无用了,没关系,我们可以使用一个字节来记录提交次数,只是在业务上需要处理好十进制和二进制直接的转换而已。

生成模拟数据
public void genTestData() {
    if(redisUtils.isExist(CommonConstant.KEY)){
        return;
    }
    // 获取当前年的总天数
    int days = getDays();
    for (int i = 0; i < days; i++) {
        int random = ThreadLocalRandom.current().nextInt(64);
        // 生成随机数表示每天的PR次数
        String binaryString = Integer.toBinaryString(random);
        if (binaryString.length() < 8) {
            // 填充0
            if(binaryString.length() == 0){binaryString = "00000000";}
            else if(binaryString.length() == 1){binaryString = "0000000"+binaryString;}
            else if(binaryString.length() == 2){binaryString = "000000"+binaryString;}
            else if(binaryString.length() == 3){binaryString = "00000"+binaryString;}
            else if(binaryString.length() == 4){binaryString = "0000"+binaryString;}
            else if(binaryString.length() == 5){binaryString = "000"+binaryString;}
            else if(binaryString.length() == 6){binaryString = "00"+binaryString;}
            else if(binaryString.length() == 7){binaryString = "0"+binaryString;}
        }
        char[] chars = binaryString.toCharArray();
        for (int j = 0; j < chars.length; j++) {
            // 设置BitMap
            redisUtils.setBit(CommonConstant.KEY,i*8+j,chars[j]);
        }
    }}private int getDays(){
    Calendar calOne = Calendar.getInstance();
    int year = calOne.get(Calendar.YEAR);
    System.out.println(year);
    Calendar calTwo = new GregorianCalendar(year, 11, 31);
    return calTwo.get(Calendar.DAY_OF_YEAR);}
获取数据
public List<String> getPushData() {
    List<String> res = new ArrayList<>(366);
    // 没有数据就先造数据
    genTestData();
    int days = getDays();
    for(long i=0;i<days;i++){
        StringBuilder sb = new StringBuilder();
        for (int j = 0; j < 8; j++) {
            String bit = redisUtils.getBit(CommonConstant.KEY, i * 8 + j);
            sb.append(bit);
        }
        // 直接返回二进制串,前端转换为十进制
        res.add(sb.toString());
    }
    return res;}

这里觉得可以直接将所有的bit统统返回,在前端进行分割处理

前端渲染
<script type="text/javascript">
    var chartDom = document.getElementById('main');
    var myChart = echarts.init(chartDom);
    var option;

    function getVirtulData(year) {
        var date = +echarts.number.parseDate(year + '-01-01');
        var end = +echarts.number.parseDate(+year + 1 + '-01-01');
        var dayTime = 3600 * 24 * 1000;
        var data = [];
        $.ajax({
            "url":'Http://localhost:8080/test/getPushData',
            "async":false, // ajax同步获取
            success:function (res){
                for (let time = date,k=0; time < end && k < res.data.length; time += dayTime,k++) {
                    data.push([
                        echarts.fORMat.formatTime('yyyy-MM-dd', time),
                        parseInt(res.data[k],2)//客户端完成进制转换,不放在服务端完成
                    ]);
                }
            }
        })

        return data;
    }
    option = {
        title: {
            top: 30,
            left: 'left',
            text: 'BitMap Demo'
        },
        tooltip: {},
        visualMap: {
            min: 0,
            max: 32,
            type: 'piecewise',
            orient: 'horizontal',
            left: 'right',
            top: 220,
            pieces: [
                {min: 0, max: 0,label:"less"},
                {min: 1, max: 10,label:" "},
                {min: 1, max: 20,label:" "},
                {min: 21, max: 40,label:" "},
                {min: 41, max: 64,label:"more"},
            ],
            inRange: {
                color: [ '#EAEDF0', '#9AE9A8', '#41C363', '#31A14E', '#206D38' ],//颜色设置 
                colorAlpha: 0.9,//透明度
            }
        },
        calendar: {
            top: 120,
            left: 30,
            right: 30,
            cellSize: 13,
            range: '2022',
            splitLine: { show: false },//不展示边线
            itemStyle: {
                borderWidth: 0.5
            },
            yearLabel: { show: false }
        },
        series: {
            type: 'heatmap',
            coordinateSystem: 'calendar',
            data: getVirtulData('2022')
        }
    };

    option && myChart.setOption(option);</script>

感谢各位的阅读,以上就是“redis的bitmap使用实例分析”的内容了,经过本文的学习后,相信大家对redis的bitmap使用实例分析这一问题有了更深刻的体会,具体使用情况还需要大家实践验证。这里是编程网,小编将为大家推送更多相关知识点的文章,欢迎关注!

您可能感兴趣的文档:

--结束END--

本文标题: redis的bitmap使用实例分析

本文链接: https://lsjlt.com/news/72950.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

猜你喜欢
  • redis的bitmap使用实例分析
    这篇文章主要讲解了“redis的bitmap使用实例分析”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“redis的bitmap使用实例分析”吧!1.位图简介...
    99+
    2024-04-02
  • redis中的bitmap实例分析
    本文小编为大家详细介绍“redis中的bitmap实例分析”,内容详细,步骤清晰,细节处理妥当,希望这篇“redis中的bitmap实例分析”文章能帮助大家解决疑惑,下面跟着小编的思路慢慢深入,一起来学习新知识吧。1、BitMap是什么通过...
    99+
    2023-06-29
  • redis bitmap运用实例
    1.网站用户一年内的上线天数统计(活跃用户) 用户ID为key,天作为offset,上线置为1  366> 000000000000000 366 /8  50Byte  16  50 key:  u500  value:  11...
    99+
    2021-02-24
    redis bitmap运用实例
  • Redis的使用实例分析
    这篇文章主要介绍“Redis的使用实例分析”,在日常操作中,相信很多人在Redis的使用实例分析问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”Redis的使用实例分析”的疑惑...
    99+
    2024-04-02
  • Redis中Bitmap的使用示例
    目录位图应用原理位图常用命令1) SETBIT命令2) GETBIT命令3) BITCOUNT命令4)Redis Bitop 命令场景统计当日活跃用户用户签到在日常开发过程中,经常会...
    99+
    2024-04-02
  • Android中Bitmap用法实例分析
    本文实例讲述了Android中Bitmap用法。分享给大家供大家参考,具体如下: 一般在android程序中把图片文件放在res/drawable目录下就可以通过R.drawa...
    99+
    2022-06-06
    bitmap Android
  • 基于Redis分布式BitMap的应用分析
    目录一、序言二、BitMap结构1、内存消耗分析2、命令行操作BitMap3、客户端操作BitMap4、时间与空间复杂度三、BitMap应用1、回避缓存穿透2、与布隆过滤器的区别四、...
    99+
    2024-04-02
  • Redis命令使用实例分析
    这篇文章主要讲解了“Redis命令使用实例分析”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“Redis命令使用实例分析”吧!问题原因小编负责的应用是一个管理后台应用,权限管理使用 Shiro...
    99+
    2023-06-27
  • Redis使用Bitmap的方法实现
    目录1. Bitmap 是什么2. 占用存储空间3. 命令3.1 SETBIT3.2 GETBIT3.3 BITCOUNT3.4 BITOP3.5 BITPOS1. Bitmap 是什么 Bitmap(也称为位数组或者位...
    99+
    2023-01-28
    Redis使用Bitmap
  • redis中的bitmap怎么使用
    Redis中的位图(bitmap)是一种特殊的数据结构,用于存储和操作位数据。它可以用于实现一些简单的计数功能,以及判断某个元素是否...
    99+
    2023-09-04
    redis bitmap
  • Redis中的Bitmap如何使用
    今天小编给大家分享一下Redis中的Bitmap如何使用的相关知识点,内容详细,逻辑清晰,相信大部分人都还太了解这方面的知识,所以分享这篇文章给大家参考一下,希望大家阅读完这篇文章后有所收获,下面我们一起来了解一下吧。在日常开发过程中,经常...
    99+
    2023-06-30
  • redis-exporter监控redis的实例分析
    本篇内容介绍了“redis-exporter监控redis的实例分析”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学...
    99+
    2024-04-02
  • php redis的scan用法实例分析
    在删除缓存的时候,我们在一些场景下需要批量删除,但不确定具体的key值,可通过匹配的方式进行查询后删除。 但是使用keys会导致redis服务器宕机。慎用。。。 一般公司也会禁用keys等比较敏感的命令的。 所以工作中会使用scan命令来进...
    99+
    2021-03-01
    php redis scan
  • Redis使用lua脚本的案例分析
    这篇文章主要介绍了Redis使用lua脚本的案例分析,具有一定借鉴价值,感兴趣的朋友可以参考下,希望大家阅读完这篇文章之后大有收获,下面让小编带着大家一起了解一下。版本:自2.6.0起可用。时间复杂度:取决...
    99+
    2024-04-02
  • Redis的zmalloc函数实例分析
    这篇“Redis的zmalloc函数实例分析”文章的知识点大部分人都不太理解,所以小编给大家总结了以下内容,内容详细,步骤清晰,具有一定的借鉴价值,希望大家阅读完这篇文章能有所收获,下面我们一起来看看这篇“...
    99+
    2024-04-02
  • Redis中bitmap的用法
    本篇内容介绍了“Redis中bitmap的用法”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成! ...
    99+
    2024-04-02
  • Redis中安装与使用的示例分析
    这篇文章主要介绍Redis中安装与使用的示例分析,文中介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们一定要看完!1、下载:项目地址:https://github.com/MSOpenTech/redi...
    99+
    2024-04-02
  • Vuex的使用实例分析
    这篇文章主要介绍“Vuex的使用实例分析”的相关知识,小编通过实际案例向大家展示操作过程,操作方法简单快捷,实用性强,希望这篇“Vuex的使用实例分析”文章能帮助大家解决问题。 Vuex 简介 1. 概...
    99+
    2024-04-02
  • Redis持久化实例分析
    本篇内容介绍了“Redis持久化实例分析”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!面试官:小伙子,谈谈...
    99+
    2024-04-02
  • redis单节点实例分析
    这篇文章主要讲解了“redis单节点实例分析”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“redis单节点实例分析”吧!1.安装jdk1.8[root@sht-sgmhadoopdn-04 ...
    99+
    2023-06-03
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作