日韩黑丝制服一区视频播放|日韩欧美人妻丝袜视频在线观看|九九影院一级蜜桃|亚洲中文在线导航|青草草视频在线观看|婷婷五月色伊人网站|日本一区二区在线|国产AV一二三四区毛片|正在播放久草视频|亚洲色图精品一区

分享

【Redis07】Redis基礎(chǔ):Bitmap 與 HyperLogLog 相關(guān)操作

 硬核項(xiàng)目經(jīng)理 2023-03-27 發(fā)布于湖南

Redis基礎(chǔ)學(xué)習(xí):Bitmap 與 HyperLogLog 相關(guān)操作

繼續(xù)進(jìn)行 Redis 基礎(chǔ)部分的學(xué)習(xí),今天我們學(xué)習(xí)的是兩種另外的數(shù)據(jù)類型。說是數(shù)據(jù)類型,但其實(shí)它們實(shí)際上使用的都是 String 類型做為底層基礎(chǔ),只不過是在存儲(chǔ)的時(shí)候進(jìn)行了一些特殊的操作。換句話說,這兩種類型并不是真正意義上的“數(shù)據(jù)類型”,換成“數(shù)據(jù)操作”可能更合適一些。

Bitmap

先來看看 Bitmap ,這是個(gè)啥玩意?其實(shí)呀,這個(gè)就是 位圖 ,Bit 就是 比特 的意思嘛。一提到 比特 是不是又得來二進(jìn)制了?這個(gè)真沒辦法,學(xué)計(jì)算機(jī)的,任何工具都逃不掉的就是 二進(jìn)制 相關(guān)的操作。不過在 Redis 中的操作還是比較簡(jiǎn)單的,畢竟我們主要的操作還是在程序業(yè)務(wù)端進(jìn)行,Redis 只是一個(gè)數(shù)據(jù)庫(kù)而已。

Bitmap 在 Redis 中可以存儲(chǔ)大約 40 多億條數(shù)據(jù),或者說是40多億個(gè) 0 和 1 位。估計(jì)不少小伙伴馬上就能想到它的應(yīng)用場(chǎng)景了,我們?cè)谧詈笤僬f。先來看看它的操作。

Bitmap 操作命令

設(shè)置和獲取指定位置的位信息,直接使用 SETBIT 和 GETBIT。

127.0.0.1:6379> setbit a 8 1
(integer) 0
127.0.0.1:6379> setbit a 4 1
(integer) 0
127.0.0.1:6379> get a
"\b\x80"
127.0.0.1:6379> getbit a 4
(integer) 1
127.0.0.1:6379> getbit a 5
(integer) 0

注意到上面的例子,我們直接使用 GET 命令也可以獲取到內(nèi)容,前面就說過了,它本身就是使用 String 類型存儲(chǔ)的。只不過使用位操作的話,我們要理解 8 位代表 1 個(gè)字節(jié)的問題。因此,設(shè)置一個(gè) 8 位的二進(jìn)制代碼才能表示出一個(gè)我們能看懂的字符。

127.0.0.1:6379> setbit b 1 1
(integer) 0
127.0.0.1:6379> setbit b 4 1
(integer) 0
127.0.0.1:6379> setbit b 5 1
(integer) 0
127.0.0.1:6379> setbit b 6 1
(integer) 0
127.0.0.1:6379> setbit b 7 1
(integer) 0
127.0.0.1:6379> get b
"O"
127.0.0.1:6379> setbit c 1 1
(integer) 0
127.0.0.1:6379> setbit c 2 1
(integer) 0
127.0.0.1:6379> setbit c 4 1
(integer) 0
127.0.0.1:6379> setbit c 6 1
(integer) 0
127.0.0.1:6379> setbit c 7 1
(integer) 0
127.0.0.1:6379> get c
"k"

看出上面的例子是什么意思了嗎?第一個(gè) b ,設(shè)置的其實(shí)是 01001111 ,轉(zhuǎn)換成十進(jìn)制是 79 ,對(duì)應(yīng)的 ASC2 碼就是大寫英文 O 。另一個(gè) c 也是一樣的意思,01101011 的十進(jìn)制是 107 ,表示的英文是 107 。如果要表示 Ok 這兩個(gè)字母的話,就需要兩個(gè) 8 位,也就是兩個(gè)字節(jié) 01001111 01101011 。

127.0.0.1:6379> setbit d 1 1
(integer) 0
127.0.0.1:6379> setbit d 4 1
(integer) 0
127.0.0.1:6379> setbit d 5 1
(integer) 0
127.0.0.1:6379> setbit d 6 1
(integer) 0
127.0.0.1:6379> setbit d 7 1
(integer) 0
127.0.0.1:6379> get d
"O"
127.0.0.1:6379> setbit d 9 1
(integer) 0
127.0.0.1:6379> setbit d 10 1
(integer) 0
127.0.0.1:6379> setbit d 12 1
(integer) 0
127.0.0.1:6379> setbit d 14 1
(integer) 0
127.0.0.1:6379> setbit d 15 1
(integer) 0
127.0.0.1:6379> get d
"Ok"

好玩吧?中文也是一樣的,只是三個(gè)字節(jié)的 UTF8 編碼,需要三個(gè) 8 位的數(shù)據(jù)才能表示一個(gè)中文字,大家可以試試哦。

127.0.0.1:6379> set s 中
OK
127.0.0.1:6379> get s
"\xe4\xb8\xad"
127.0.0.1:6379> getbit s 0
(integer) 1
127.0.0.1:6379> getbit s 1
(integer) 1
127.0.0.1:6379> getbit s 2
(integer) 1
127.0.0.1:6379> getbit s 3
(integer) 0

“中”這個(gè)字直接 GET 返回的是16進(jìn)制數(shù)據(jù),將 e4b8ad 轉(zhuǎn)換成二進(jìn)制是 11100100 10111000 10101101 ,然后我們就可以使用 GETBIT 來驗(yàn)證是不是和我們手動(dòng)轉(zhuǎn)換出來的是一樣的結(jié)果。

大家有興趣的可以再多了解一下字符編碼相關(guān)的知識(shí),比如在 Redis 中使用的 UTF8 ,為什么單個(gè)字節(jié)第一位只能是 0 ,必須是 0xxxxxxx 這樣的,中文也都是固定的 1110xxxx 10xxxxxx 10xxxxxx 這種格式,很有意思哦。

位操作

既然是位操作,那么 與、或、異或、非 操作肯定要有啦。

127.0.0.1:6379> bitop or dd b c
(integer) 1
127.0.0.1:6379> get dd
"o"
127.0.0.1:6379> bitop and dd b c
(integer) 1
127.0.0.1:6379> get dd
"K"
127.0.0.1:6379> bitop xor dd b c
(integer) 1
127.0.0.1:6379> get dd
"$"
127.0.0.1:6379> bitop not dd b
(integer) 1
127.0.0.1:6379> get dd
"\xb0"

查詢第一個(gè)bit位位置

通過 BITPOS 命令,可以查詢到指定的 key 中,第一個(gè)出現(xiàn)的位數(shù)據(jù)的位置。

127.0.0.1:6379> BITPOS a 1
(integer) 4
127.0.0.1:6379> BITPOS a 0
(integer) 0

第一條命令是查詢第一個(gè) 1 出現(xiàn)的位置,第二條命令是查詢第一個(gè) 0 出現(xiàn)的位置。它還有第二個(gè)參數(shù),這個(gè)參數(shù)是一個(gè)偏移量,不過需要注意的是,它指的是字節(jié)的偏移量,不是位的偏移量。

127.0.0.1:6379> BITPOS d 1 0
(integer) 1
127.0.0.1:6379> BITPOS d 1 1
(integer) 9
127.0.0.1:6379> BITPOS d 1 2
(integer) -1

統(tǒng)計(jì)數(shù)量

BITCOUNT 可以統(tǒng)計(jì)指定 key 中 1 出現(xiàn)的次數(shù)。

127.0.0.1:6379> BITCOUNT d
(integer) 10
127.0.0.1:6379> BITCOUNT d 1 0 2
(error) ERR syntax error
127.0.0.1:6379> BITCOUNT d 1 0 1
(error) ERR syntax error
127.0.0.1:6379> BITCOUNT d 1 2
(integer) 5
127.0.0.1:6379> BITCOUNT d 0 2
(integer) 10
127.0.0.1:6379> BITCOUNT d 2 3
(integer) 0

它還有兩個(gè)參數(shù),同樣也是字節(jié)偏移量和長(zhǎng)度。

位域操作

位域這個(gè)東西我就不太懂了,只是給個(gè)例子,這一塊深入學(xué)過 C 的同學(xué)應(yīng)該會(huì)比較了解。

127.0.0.1:6379> setbit e 1 1
(integer) 0
127.0.0.1:6379> get e
"@"
127.0.0.1:6379> BITFIELD e incrby i5 100 1 get u4 0
1) (integer) 1
2) (integer) 4
127.0.0.1:6379> get e
"@\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x80"

Bitmap 經(jīng)典例子

好了,Bitmap 的基本操作就是上面那些,介紹的比較簡(jiǎn)單,不過也確實(shí)都不復(fù)雜。但前提是要對(duì)二進(jìn)制和進(jìn)制之間的轉(zhuǎn)換以及字符編碼要有一定的了解。如果我們只是為了去顯示字符串的位,那就有點(diǎn)大材小用了,其實(shí) Bitmap 有個(gè)非常強(qiáng)悍的能力,也就是運(yùn)用 BITCOUNT 去進(jìn)行數(shù)據(jù)統(tǒng)計(jì)。

包括官網(wǎng)上給出的也是類似這樣的例子,統(tǒng)計(jì)登錄用戶數(shù)。

最開始我們已經(jīng)知道,一個(gè) key 可以保存40多億個(gè)位,那么我們可以把用戶id當(dāng)作位索引,然后某個(gè)用戶今天登錄了,就給它的位設(shè)置為 1 ,然后就可以 BITCOUNT 快速統(tǒng)計(jì)出今天有多少用戶登錄了系統(tǒng),速度相當(dāng)快哦。

127.0.0.1:6379> setbit user_login_20220509 100010 1
(integer) 0
127.0.0.1:6379> setbit user_login_20220509 25525 1
(integer) 0
127.0.0.1:6379> setbit user_login_20220509 8782 1
(integer) 0
127.0.0.1:6379> setbit user_login_20220509 9846526547 1
(error) ERR bit offset is not an integer or out of range
127.0.0.1:6379> setbit user_login_20220509 98465265 1
(integer) 0
127.0.0.1:6379> setbit user_login_20220509 399999999 1
(integer) 0
127.0.0.1:6379> setbit user_login_20220509 3999999999 1
(integer) 0
127.0.0.1:6379> setbit user_login_20220509 4294967295 1
(integer) 0
127.0.0.1:6379> setbit user_login_20220509 4294967296 1
(error) ERR bit offset is not an integer or out of range
127.0.0.1:6379> BITCOUNT user_login_20220509
(integer) 7

HyperLogLog

既然說到統(tǒng)計(jì)了,那么 HyperLogLog 這個(gè)操作類型就不得不提了。它是一種概率數(shù)據(jù)結(jié)構(gòu),用于計(jì)算唯一事物的集合數(shù)量。同時(shí),它是以內(nèi)存換精度的,也就是說,它能極大的節(jié)約內(nèi)存,但是會(huì)有精度丟失的問題。最壞情況下,也就是內(nèi)存占用最大的情況下,它也只需要 12K 的內(nèi)存容量就可以存儲(chǔ)非常巨大的數(shù)據(jù)量,精度的丟失會(huì)在 1% 以內(nèi)。它也可以實(shí)現(xiàn)上面 Bitmap 中統(tǒng)計(jì)的例子,我們先來看看相關(guān)的操作命令。

127.0.0.1:6379> pfadd hll a b c d e f g
(integer) 1
127.0.0.1:6379> pfcount hll
(integer) 7
127.0.0.1:6379> pfadd hll a b c d e f g h i j k
(integer) 1
127.0.0.1:6379> pfcount hll
(integer) 11

PFADD 添加數(shù)據(jù),PFCOUNT 獲取數(shù)量?;镜牟僮髅罹瓦@兩個(gè),是不是無敵了。除了這兩個(gè)外,還有一個(gè)合并的命令,類似于集合的并集操作。

127.0.0.1:6379> PFADD pfa a b c d e f
(integer) 1
127.0.0.1:6379> PFADD pfb d c a e g i
(integer) 1
127.0.0.1:6379> pfadd pfc b d f i l m n o
(integer) 1
127.0.0.1:6379> PFMERGE pfmerge pfa pfb pfc
OK
127.0.0.1:6379> pfcount pfmerge
(integer) 12

另外,它也是以 String 為基本存儲(chǔ)類型的,所以用 GET 也能看到內(nèi)容。

127.0.0.1:6379> get hll
"HYLL\x01\x00\x00\x00\x0b\x00\x00\x00\x00\x00\x00\x00Fm\x80I\xe8\x80L\"\x80D<\x848\x80B=\x80K\x83\x80B\xed\x84A\xfc\x8cG\x8e\x80Bm\x80BZ"

好了,接下來說下 HyperLogLog 和其它方式統(tǒng)計(jì)的對(duì)比,以下是網(wǎng)上找到的相關(guān)文章獲取到的資料。

如果我們使用 SET 來進(jìn)行基數(shù)統(tǒng)計(jì),那么假設(shè)每一個(gè)元素的 32Bit(2^24 ≈ 1600萬(wàn); 2^32 ≈ 42億) , 假設(shè)存儲(chǔ)1億個(gè)不重復(fù)的元素那么我們需要 100 000 000 * 32 /8/1024/1024 ≈ 381MB。

如果我們用 Bitmap 來進(jìn)行基數(shù)統(tǒng)計(jì),每個(gè)元素對(duì)應(yīng)一位(bit),假設(shè)我們存儲(chǔ)1億個(gè)不重復(fù)的元素那么我們需要 100 000 000 /8/1024/1024 ≈ 12MB。

然而我們使用 HyperLogLog ,一個(gè)鍵占用的內(nèi)容空間是12KB,并且這個(gè)鍵可以處理海量數(shù)據(jù)。

它們?nèi)齻€(gè)都可以應(yīng)用在統(tǒng)計(jì)不重復(fù)元素的場(chǎng)景:

  • HyperLogLog:海量數(shù)據(jù),可以忍受 0.81% 誤差的場(chǎng)景,其實(shí)大數(shù)據(jù)處理的時(shí)候都會(huì)有誤差。
  • Bitmap:數(shù)據(jù)量不大,不能忍受誤差,元素連續(xù)的(自增的用戶主鍵id)。
  • SET:數(shù)據(jù)量不大,不能忍受誤差,元素沒有規(guī)律,并且需要返回實(shí)際的單個(gè)元素。

總結(jié)

今天的內(nèi)容挺好玩吧,Bitmap 和 HyperLogLog 最常用的其實(shí)都是一個(gè)不重復(fù)數(shù)據(jù)統(tǒng)計(jì)的場(chǎng)景,但是又各有優(yōu)勢(shì)。在日常的工作中如果有類似的應(yīng)用場(chǎng)景,完全就可以使用這兩種數(shù)據(jù)操作來試試了。

擴(kuò)展知識(shí):布隆過濾器

布隆過濾器(Bloom Filter),聽說過沒?面試有沒有被坑過?跟你說,布隆過濾器的基礎(chǔ)知識(shí)就是 Bitmap ,HyperLogLog 也是它的類似實(shí)現(xiàn)之一。啥叫布隆過濾器?就是能夠快速地查找某一個(gè)元素是否存在于指定的集合中,最典型的做法就是使用二進(jìn)制位來進(jìn)行操作,這不就是 Bitmap 嘛。

當(dāng)然,完整的布隆過濾器的實(shí)現(xiàn)還是要更復(fù)雜一些,它一般會(huì)有三個(gè) Hash 函數(shù),生成三個(gè)不同位置,只有當(dāng)三個(gè)位置全部命中時(shí),才會(huì)認(rèn)為指定的數(shù)據(jù)已經(jīng)存在。從這一點(diǎn)上來說,其實(shí)就是在有限的空間內(nèi)可以存放更多的數(shù)據(jù)。但它也會(huì)出現(xiàn)不同的數(shù)據(jù)三個(gè) Hash 函數(shù)計(jì)算結(jié)果一致的概率,但我們可以增加更多的 Hash 函數(shù),不過相應(yīng)地性能也會(huì)降低。同樣,它也會(huì)有精度問題,反正大概原理就是這樣。布隆過濾器有一個(gè)非常經(jīng)典的名言,那就是“我說你不在,那你一定不在;我說你存在,你有可能存在(也可能不存在)!”。在 packgist 上也能搜到純 PHP 實(shí)現(xiàn)布隆過濾器的 Composer 包,大家可以自己下載源碼學(xué)習(xí)一下哦!

    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評(píng)論

    發(fā)表

    請(qǐng)遵守用戶 評(píng)論公約

    類似文章 更多