Redis基於Bitmap實現使用者簽到功能

2022-09-24 10:15:10 字數 3992 閱讀 5433

目錄

很多應用上都有使用者簽到的功能,尤其是配合積分系統一起使用。現在有以下需求:

對於使用者簽到資料,如果直接採用資料庫儲存,當出現高併發訪問時,對資料庫壓力會很大,例如雙十一簽到活動。這時候應該採用快取,以減輕資料庫的壓力,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相關命令。雖然是新命令,但是並沒有新增...