美文网首页
节衣缩食 —— 位图

节衣缩食 —— 位图

作者: DreamsonMa | 来源:发表于2019-03-21 20:29 被阅读0次

    在我们平时开发过程中,会有一些 bool 型数据需要存取,比如用户一年的签到记录,签了是 1,没签是 0,要记录 365 天。如果使用普通的 key/value,每个用户要记录 365 个,当用户上亿的时候,需要的存储空间是惊人的。

    为了解决这个问题,Redis 提供了位图数据结构,这样每天的签到记录只占据一个位,365 天就是 365 个位,46 个字节 (一个稍长一点的字符串) 就可以完全容纳下,这就大大节约了存储空间。

    位图数据结构

    Redis 的位数组是自动扩展,如果设置了某个偏移位置超出了现有的内容范围,就会自动将位数组进行零扩充。

    Bit实例

    下面实现一个签到的例子:
    考虑到每月初需要重置连续签到次数,最简单的方式是按用户每月存一条签到数据(也可以每年存一条数据)。Key的格式为u:sign:uid:yyyyMM,Value则采用长度为4个字节(32位)的位图(最大月份只有31天)。位图的每一位代表一天的签到,1表示已签,0表示未签。例如u:sign:1000:201902表示ID=1000的用户在2019年2月的签到记录。

            DateTime y2 = DateUtil.parse("2019-02-01");
            String key = buildSignKey(1000, y2.toJdkDate());
    
            // 偏移量是从0开始,所以要把17减1
            jedis.setbit(key, 16, true); // 用户2月17号签到
            jedis.setbit(key, 27, true); // 用户2月28号签到
            Assert.assertTrue(jedis.getbit(key, 16)); // 检查2月17号是否签到
            Assert.assertEquals(2,jedis.bitcount(key).longValue()); // 统计2月份的签到次数
            // u8,一个8位的无符号整数,i16是一个16位的有符号整数
            List<Long> list = jedis.bitfield(key, "GET", "u28", "0");// 获取2月份前28天的签到数据
            Map<String, Boolean> signMap = new HashMap(DateUtil.dayOfMonth(y2.toJdkDate()));
            if (list != null && list.size() > 0) {
                // 由低位到高位,为0表示未签,为1表示已签
                long v = list.get(0) == null ? 0 : list.get(0);
                for (int i = 28; i > 0; i--) {
                    final DateTime dateTime = DateUtil.offsetDay(y2.toJdkDate(), i-1);
                    signMap.put(DateUtil.format(dateTime, "yyyy-MM-dd"), v >> 1 << 1 != v);
                    v >>= 1;
                }
            }
            Console.log(JSONUtil.toJsonPrettyStr(signMap));
            Assert.assertEquals(16, jedis.bitpos(key, true).longValue()); // 获取当月首次签到日期
    

    打印信息如下:

    {
        "2019-02-09": false,
        "2019-02-08": false,
        "2019-02-07": false,
        "2019-02-28": true,
        "2019-02-06": false,
        "2019-02-27": false,
        "2019-02-05": false,
        "2019-02-26": false,
        "2019-02-04": false,
        "2019-02-25": false,
        "2019-02-03": false,
        "2019-02-24": false,
        "2019-02-02": false,
        "2019-02-23": false,
        "2019-02-01": false,
        "2019-02-22": false,
        "2019-02-21": false,
        "2019-02-20": false,
        "2019-02-19": false,
        "2019-02-18": false,
        "2019-02-17": true,
        "2019-02-16": false,
        "2019-02-15": false,
        "2019-02-14": false,
        "2019-02-13": false,
        "2019-02-12": false,
        "2019-02-11": false,
        "2019-02-10": false
    }
    

    本文基于《Redis深度历险:核心原理和应用实践》一文的JAVA实践。更多文章请参考:高性能缓存中间件Redis应用实战(JAVA)

    相关文章

      网友评论

          本文标题:节衣缩食 —— 位图

          本文链接:https://www.haomeiwen.com/subject/mqmcvqtx.html