bitmap簡(jiǎn)介
8 個(gè) bit 組成一個(gè) Byte,所以 bitmap 極大的節(jié)省儲(chǔ)存空間
你可以把它理解為一個(gè)特殊處理過的 字符串
key代表業(yè)務(wù)屬性、標(biāo)簽。一個(gè) bit 位來表示某個(gè)元素對(duì)應(yīng)的值或者狀態(tài)。
bitmap 并不是一種數(shù)據(jù)結(jié)構(gòu),實(shí)際上它就是字符串,但是可以對(duì)字符串的位進(jìn)行操作。
bitmap有自己的一套命令。可以把bitmap想象成一個(gè)以bit為單位的數(shù)組,數(shù)組的每個(gè)單元存儲(chǔ)0和1,數(shù)組的下標(biāo)叫做偏移量。
Redis 提供 setbit,getbit,bitcount等幾個(gè) bitmap 相關(guān)命令。但其實(shí) setbit 等命令只不過是在 set 上的擴(kuò)展而已。
setbit 命令介紹
指令
SETBIT key offset value
復(fù)雜度 O(1)
設(shè)置或者清空 key 的 value(字符串)在 offset 處的 bit 值(只能只 0 或者 1)。
空間占用、以及第一次分配空間需要的時(shí)間
在一臺(tái) 2010MacBook Pro 上
offset 為 2^32-1(分配 512MB)需要~ 300ms
offset 為 2^30-1(分配 128MB)需要~ 80ms
offset 為 2^28-1(分配 32MB)需要~ 30ms
offset 為 2^26-1(分配 8MB)需要 8ms。
– <來自官方文檔: https://redis.io/commands/setbit>
大概的空間占用計(jì)算公式是:($offset/8/1024/1024)MB
Available since 2.2.0.
Time complexity: O(1)
Sets or clears the bit at offset in the string value stored at key.
The bit is either set or cleared depending on value, which can be either 0 or 1.
When key does not exist, a new string value is created. The string is grown to make sure it can hold a bit at offset. The offset argument is required to be greater than or equal to 0, and smaller than 232 (this limits bitmaps to 512MB). When the string at key is grown, added bits are set to 0.
Warning: When setting the last possible bit (offset equal to 232 -1) and the string value stored at key does not yet hold a string value, or holds a small string value, Redis needs to allocate all intermediate memory which can block the server for some time. On a 2010 MacBook Pro, setting bit number 232 -1 (512MB allocation) takes ~300ms, setting bit number 230 -1 (128MB allocation) takes ~80ms, setting bit number 228 -1 (32MB allocation) takes ~30ms and setting bit number 226 -1 (8MB allocation) takes ~8ms. Note that once this first allocation is done, subsequent calls to SETBIT for the same key will not have the allocation overhead.
Redis bitmap 的命令
bitmap的命令
常用命令 作用
1、getbit key offset
用于獲取Redis中指定key對(duì)應(yīng)的值,中對(duì)應(yīng)offset的bit
2、setbit key key offset value
用于修改指定key對(duì)應(yīng)的值,中對(duì)應(yīng)offset的bit
3、 bitcount key [start end]
用于統(tǒng)計(jì)字符串被設(shè)置為1的bit數(shù)
4、bitop and/or/xor/not destkey key [key …]
用于對(duì)多個(gè)key求邏輯與/邏輯或/邏輯異或/邏輯非
設(shè)置 offset 位的 01 值
127.0.0.1:6379> setbit user10001 0 1
(integer) 0
127.0.0.1:6379> setbit user10001 3 1
(integer) 0
127.0.0.1:6379> getbit user10001 0
(integer) 1
127.0.0.1:6379> getbit user10001 1
(integer) 0
127.0.0.1:6379> getbit user10001 2
(integer) 0
127.0.0.1:6379> getbit user10001 3
(integer) 1
127.0.0.1:6379> getbit user10001 7
(integer) 1
批量設(shè)置 offset 位的 01 值
使用 u1 類型批量設(shè)置 offset 位的 01 值:
127.0.0.1:6379> bitfield user10001 set u1 2 1 set u1 9 1 set u1 10 1
1) (integer) 0
2) (integer) 0
3) (integer) 0
127.0.0.1:6379> getbit user10001 10
(integer) 1
127.0.0.1:6379> getbit user10001 9
(integer) 1
127.0.0.1:6379> getbit user10001 8
(integer) 0
127.0.0.1:6379> getbit user10001 7
(integer) 1
bitmap的應(yīng)用場(chǎng)景
使用經(jīng)驗(yàn):
type = string,BitMap 是 sting 類型,最大 512 MB。
注意 setbit 時(shí)的偏移量,可能有較大耗時(shí)
位圖不是絕對(duì)好。
通過 bitcount可以很快速的統(tǒng)計(jì),比傳統(tǒng)的關(guān)系型數(shù)據(jù)庫(kù)效率高很多
1、比如統(tǒng)計(jì)年活躍用戶數(shù)量
用戶的ID作為offset,當(dāng)用戶在一年內(nèi)訪問過網(wǎng)站,就將對(duì)應(yīng)offset的bit值設(shè)置為“1”;
通過bitcount 來統(tǒng)計(jì)一年內(nèi)訪問過網(wǎng)站的用戶數(shù)量
2、比如統(tǒng)計(jì)三天內(nèi)活躍用戶數(shù)量
時(shí)間字符串作為key,比如 “190108:active“ “190109:active”“190110:active” ;
用戶的ID就可以作為offset,當(dāng)用戶訪問過網(wǎng)站,就將對(duì)應(yīng)offset的bit值設(shè)置為“1”;
統(tǒng)計(jì)三天的活躍用戶,通過bitop or 獲取一周內(nèi)訪問過的用戶數(shù)量
3、連續(xù)三天訪問的用戶數(shù)量 bitop and
4、三天內(nèi)沒有訪問的用戶數(shù)量 bitop not
5、統(tǒng)計(jì)在線人數(shù) 設(shè)置在線key:“online:active”,當(dāng)用戶登錄時(shí),通過setbit設(shè)置
bitmap的優(yōu)勢(shì),以統(tǒng)計(jì)活躍用戶為例
每個(gè)用戶id占用空間為1bit,消耗內(nèi)存非常少,存儲(chǔ)1億用戶量只需要12.5M
使用場(chǎng)景: 統(tǒng)計(jì)活躍用戶
使用時(shí)間作為 cacheKey,然后用戶 ID 為 offset,如果當(dāng)日活躍過就設(shè)置為 1
那么我該如果計(jì)算某幾天/月/年的活躍用戶呢(暫且約定,統(tǒng)計(jì)時(shí)間內(nèi)只有有一天在線就稱為活躍),有請(qǐng)下一個(gè) redis 的命令
命令
BITOP operation destkey key [key ...]
說明:對(duì)一個(gè)或多個(gè)保存二進(jìn)制位的字符串 key 進(jìn)行位元操作,并將結(jié)果保存到 destkey 上。
說明:BITOP 命令支持 AND 、 OR 、 NOT 、 XOR 這四種操作中的任意一種參數(shù)
//日期對(duì)應(yīng)的活躍用戶
$data = array(
'2020-01-10' => array(1,2,3,4,5,6,7,8,9,10),
'2020-01-11' => array(1,2,3,4,5,6,7,8),
'2020-01-12' => array(1,2,3,4,5,6),
'2020-01-13' => array(1,2,3,4),
'2020-01-14' => array(1,2)
);
//批量設(shè)置活躍狀態(tài)
foreach($data as $date=>$uids) {
$cacheKey = sprintf("stat_%s", $date);
foreach($uids as $uid) {
$redis->setBit($cacheKey, $uid, 1);
}
}
$redis->bitOp('AND', 'stat', 'stat_2020-01-10', 'stat_2020-01-11', 'stat_2020-01-12');
//總活躍用戶:6
echo "總活躍用戶:" . $redis->bitCount('stat') . PHP_EOL;
$redis->bitOp('AND', 'stat1', 'stat_2020-01-10', 'stat_2020-01-11', 'stat_2020-01-14') . PHP_EOL;
//總活躍用戶:2
echo "總活躍用戶:" . $redis->bitCount('stat1') . PHP_EOL;
$redis->bitOp('AND', 'stat2', 'stat_2020-01-10', 'stat_2020-01-11') . PHP_EOL;
//總活躍用戶:8
echo "總活躍用戶:" . $redis->bitCount('stat2') . PHP_EOL;
假設(shè)當(dāng)前站點(diǎn)有 5000W 用戶,那么一天的數(shù)據(jù)大約為 50000000/8/1024/1024=6MB
布隆過濾器
bitmap - Redis布隆過濾器 (應(yīng)對(duì)緩存穿透問題)
舉例:比如爬蟲服務(wù)器在爬取電商網(wǎng)站的商品信息時(shí),首先經(jīng)過緩存,如果緩存查不到,再去數(shù)據(jù)庫(kù)獲取信息,因?yàn)榕老x的效率很高,且sku很有可能是不存在或者已下架的,就會(huì)造成緩存穿透,大量請(qǐng)求被發(fā)送到數(shù)據(jù)庫(kù),導(dǎo)致服務(wù)器受到影響。
此時(shí),可以在緩存層之前,添加一個(gè)布隆過濾器,布隆過濾器看作是一個(gè)bitmap,sku作為offset值,如果商品真實(shí)存在,bit值設(shè)為1。首先將商品數(shù)據(jù)初始化,當(dāng)有請(qǐng)求時(shí),通過getbit判斷sku是否有效。如果布隆過濾器認(rèn)為商品不存在,就拒絕訪問,這樣就可以保護(hù)存儲(chǔ)層。
Data structures are nothing different. They are like the bookshelves of your application where you can organize your data. Different data structures will give you different facility and benefits. To properly use the power and accessibility of the data structures you need to know the trade-offs of using one.
大意是不同的數(shù)據(jù)結(jié)構(gòu)有不同的適用場(chǎng)景和優(yōu)缺點(diǎn),你需要仔細(xì)權(quán)衡自己的需求之后妥善適用它們,布隆過濾器就是踐行這句話的代表。
參考資料
https://blog.csdn.net/weixin_42383575/article/details/86684283
https://zhuanlan.zhihu.com/p/136337229