redis位圖巧用,節約內存


最近要做一個聖誕抽獎活動,需要記錄每天用戶簽到的記錄,以前一般都是用普通的字符串數據類型,每個用戶的簽到用一個 key

// 用戶10在活動第一天的簽到key為record:1:10
$key = "record:$day:$id";
if ($redis->get($key)) {
    echo '已簽到';
} else {
    $redis->set($key, 1)
}

那么一個用戶一天的簽到記錄就要占一個字節,用戶一多就產生非常多的 key,浪費寶貴的內存。

位圖

為了解決這個問題,redis 另一種數據類型位圖就非常適合。位圖並不是特殊的數據類型,內容其實就是字符串,每一位只存儲0或1,非常適合存儲這種布爾類型的數據

位圖使用 setbit/getbit 來存取數據

> SETBIT key offset value
> GETBIT key offset

比如一個用戶聖誕連續五天的簽到記錄可以只使用一個 key, 10010 代表用戶只有第二天和第五天簽過到

$key = "record:$id";
if ($redis->getbit($key, $day)) {
    echo '已簽到';
} else {
    $redis->setbit($key, $day, 1)
}

現在一個用戶五天的簽到記錄只會產生一個 key,占用內存僅為 5bit 不到一個字節

進一步,如果你的用戶系統中用戶 id 是連續的 int 類型,還能更節省。因為只記錄每個用戶5天的簽到記錄,在一串位圖中,每個用戶占5個坑,這樣所有的用戶的簽到數據只會使用一個 key

// 用戶1占前5個坑
$offset = ($id - 1) * 5 + $day -1;
if ($redis->getbit('record', $offset)) {
    echo '已簽到';
} else {
    $redis->setbit('record', $offset, 1)
}

現在只需要一個 key 就可以存下所有用戶的簽到記錄了。

需要注意的是位圖一個 key 最多存儲 512mb 的內容,如果你的用戶數大於 8*1024*1024*1024*512 / 5 ≈ 87 億 並不適用這個方法。

其他用法

bitcount 用來統計指定位置范圍內 1 的個數,bitpos 用來查找指定范圍內出現的第一個 0 或 1。

> setbit s 0 1
> setbit s 3 1     #s=1001
> bitcount s [start, end]
(integer) 2
> bitpos s 0 [start, end]
(integer) 1


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM