在咱们平时开发过程当中,会有一些 bool 型数据须要存取,好比用户一年的签到记录,签了是 1,没签是 0,要记录 365 天。若是使用普通的 key/value,每一个用户要记录 365 个,当用户上亿的时候,须要的存储空间是惊人的。数组
为了解决这个问题,Redis 提供了位图数据结构,这样天天的签到记录只占据一个位,365 天就是 365 个位,46 个字节 (一个稍长一点的字符串) 就能够彻底容纳下,这就大大节约了存储空间。缓存
Redis 的位数组是自动扩展,若是设置了某个偏移位置超出了现有的内容范围,就会自动将位数组进行零扩充。数据结构
下面实现一个签到的例子:
考虑到每个月初须要重置连续签到次数,最简单的方式是按用户每个月存一条签到数据(也能够每一年存一条数据)。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()); // 获取当月首次签到日期
打印信息以下:ui
{ "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)code