1. 程式人生 > >redis點陣圖巧用,節約記憶體

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