目錄
很多應用上都有使用者簽到的功能,尤其是配合積分系統一起使用。現在有以下需求:
對於使用者簽到資料,如果直接採用資料庫儲存,當出現高併發訪問時,對資料庫壓力會很大,例如雙十一簽到活動。這時候應該採用快取,以減輕資料庫的壓力,redis是高效能的記憶體資料庫,適用於這樣的場景。
如果採用string型別儲存,當使用者數量大時,記憶體開銷就非常大。
如果採用集合型別儲存,例如set、hash,查詢使用者某個範圍的資料時,查詢效率又不高。
redis提供的資料型別bitmap(位圖),每個bit位對應0和1兩個狀態。雖然內部還是採用string型別儲存,但redis提供了一些指令用於直接操作bitmap,可以把它看作乙個bit陣列,陣列的下標就是偏移量。
它的優點是記憶體開銷小,效率高且操作簡單,很適合用於簽到這類場景。缺點在於位計算和位表示數值的侷限。如果要用位來做業務資料記錄,就不要在意value的值。
redis提供了以下幾個指令用於操作bitmap:
命令說明
可用版本
時間複雜度
lpdqyr
setbit
對 key 所儲存的字串值,設定或清除指定偏移量上的位(bit)。
>= 2.2.0
o(1)
getbit
對 key 所儲存的字串值,獲取指定偏移量上的位(bit)。
>= 2.2.0
o(1)
bitcount
計算給定字串中,被設定為 1 的位元位的數量。
>= 2.6.0
o(n)
bitpos
返回點陣圖中第乙個值為 bit 的二進位制位的位置。
>= 2.8.7
o(n)
bitop
對乙個或多個儲存二進位制位的字串 key 進行位元操作。
>= 2.6.0
o(n)
bitfield
bitfield 命令可以在一次呼叫中同時對多個位範圍進行操作。
>= 3.2.0
o(1)
考慮到每月要重置連續簽到次數,最簡單的方式是按使用者每月存一條簽到資料。key的格式為 u:sign::,而value則採用長度為4個位元組的(32位)的bitmap(最大月份只有31天)。bitmap的每一位代表一天的簽到,1表示已簽,0表示未簽。
例如 u:sign:1225:202101 表示id=1225的使用者在2023年1月的簽到記錄
# 使用者1月6號簽到
setbit u:sign:1225:202101 5lpdqyr 1 # 偏移量是從0開始,所以要把6減1
# 檢查1月6號是否簽到
getbit u:sign:1225:202101 5 # 偏移量是從0開始,所以要把6減1
# 統計1月份的簽到次數
bitcount u:sign:1225:202101
# 獲取1月份前31天的簽到資料
bitfield u:sign:1225:202101 get u31 0
# 獲取1月份首次簽到的日期
bitpos u:sign:1225:202101 1 # 返回的首次簽到的偏移量,加上1即為當月的某一天
示例**
using stackexchange.redis;
using system;
using systlpdqyrem.collections.generic;
using system.linq;
/*** 基於redis bitmap的使用者簽到功能實現類
* * 實現功能:
* 1. 使用者簽到
* 2. 檢查使用者是否簽到
* 3. 獲取當月簽到次數
* 4. 獲取當月連續簽到次數
* 5. 獲取當月首次簽到日期
* 6. 獲取當月簽到情況
*/public class usersigndemo
/*** 使用者簽到
** @param uid 使用者id
* @param date 日期
* @return 之前的簽到狀態
*/public bool dosign(int uid, datetime date)
/*** 檢查使用者是否簽到
** @param uid 使用者id
* @param date 日期
* @return 當前的簽到狀態
*/public bool checksign(int uid, datetime date)
/*** 獲取使用者簽到次數
** @param uid 使用者id
* @param date 日期
* @return 當前的簽到次數
*/public long getsigncount(int uid, datetime date)
/*** 獲取當月連續簽到次數
** @param uid 使用者id
* @param date 日期
* @return 當月連續簽到次數
*/public long getcontinuoussigncount(int uid, datetime date)
"; // 取1號到當天的簽到狀態
redisresult result = _db.execute("bitfield", (rediskey)buildsignkey(uid, date), "get", type, 0);
if (!result.isnull)
else
v >>= 1;}}
}return signcount;
}/**
* 獲取當月首次簽到日期
** @param uid 使用者id
* @param date 日期
* @return 首次簽到日期
*/public datetime? getfirstsigndate(int uid, datetime date)
/*** 獲取當月簽到情況
** @param uid 使用者id
* @param date 日期
* @return key為簽到日期,value為簽到狀態的map
*/public dictionary getsigninfo(int uid, datetime date)
";redisresult result = _db.execute("bitfield", (rediskey)buildsignkey(uid, date), "get", type, 0);
if (!result.isnull)}}
return signmap;
}private static string formatdate(datetime date)
private static string formatdate(datetime date, string pattern)
/*** 構建簽到key
** @param uid 使用者id
* @param date 日期
* @return 簽到key
*/private static string buildsignkey(int uid, datetime date)
:";}
/*** 獲取月份天數
** @param date 日期
* @return 天數
*/private static int getdayofmonth(datetime date)
if (new int .contains(date.month))
return 30;
}static void main(string args)
else
}else
}else}}
}}
執行結果
基於redis點陣圖實現使用者簽到功能
redis 深度歷險:核心原理與應用實踐
redis:bitmap的setbit,getbit,bitcount,bitop等使用與應用場景
bitfield set command is not working
Redis中bitmap的妙用
在redis中我們經常用到set,get等命令,細心的你有沒有發現,還有幾個相似的命令叫setbit,getbit,它們是用來幹嘛的?就是通過乙個bit位來表示某個元素對應的值或者狀態,其中的key就是對應元素本身。我們知道8個bit可以組成乙個byte,所以bitmap本身會極大的節省儲存空間。r...
Redis中bitmap的妙用
23k 次閱讀 讀完需要 12 分鐘 在redis中我們經常用到set,get等命令,細心的你有沒有發現,還有幾個相似的命令叫setbit,getbit,它們是用來幹嘛的?就是通過乙個bit位來表示某個元素對應的值或者狀態,其中的key就是對應元素本身。我們知道8個bit可以組成乙個byte,所以b...
Redis學習筆記(五) BitMap
就是通過乙個bit位來表示某個元素對應的值或者狀態,其中的key就是對應元素本身。我們知道8個bit可以組成乙個byte,所以bitmap本身會極大的節省儲存空間。redis從2.2.0版本開始新增了setbit,getbit,bitcount等幾個bitmap相關命令。雖然是新命令,但是並沒有新增...