在咱們平時開發過程當中,會有一些 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