redis(九:數據結構-集合)

Sorted Set 只支持范圍查詢,無法直接進行聚合計算(聚合計算是CPU密集型任務)。Set 類型可以進行聚合計算。不過,Set 的差集、并集和交集的計算復雜度較高,在數據量較大的情況下,如果直接執行這些計算,會導致 Redis 實例阻塞。
實踐上一般是把數據讀取到客戶端,在客戶端來完成聚合統計,這樣就可以規避阻塞主庫實例和其他從庫實例的風險了。(而且集群模式多個實例之間是無法做聚合運算的)

“Set數據類型,使用SUNIONSTORE、SDIFFSTORE、SINTERSTORE做并集、差集、交集時,選擇一個從庫進行聚合計算”。該說法有問題。
這3個命令都會在Redis中生成一個新key,而從庫默認是readonly不可寫的,所以這些命令只能在主庫使用。想在從庫上操作,可以使用SUNION、SDIFF、SINTER,這些命令可以計算出結果,但不會生成新key。

排序集合:List 是按照元素進入 List 的順序進行排序的,而 Sorted Set 可以根據元素的權重來排序(可做并發量不高的延時隊列,不過這個key很容易變成一個bigkey,在key過期釋放內存時可能引發阻塞風險。也可以同過時/分來拆分KEY)。

消息消費
Redis可以用作隊列,而且性能很高,部署維護也很輕量。但缺點是無法嚴格保數據的完整性(redis(二:日志)),而且內存也比較昂貴
1,List數據類型當做隊列使用,一個客戶端使用rpush生產數據到Redis中,另一個客戶端使用lpop取出數據進行消費,非常方便。但要注意的是,使用List當做隊列,缺點是沒有ack機制和不支持多個消費者。沒有ack機制會導致從Redis中取出的數據后,如果客戶端處理失敗了,取出的這個數據相當于丟失了,無法重新消費。
不過為了保證可靠性,List 類型提供了 BRPOPLPUSH 命令,這個命令的作用是讓消費者程序從一個 List 中讀取消息,同時,Redis 會把這個消息再插入到另一個 List(可以叫作備份 List)留存。這樣一來,如果消費者程序讀了消息但沒能正常處理,等它重啟后,就可以從備份 List 中重新讀取消息并進行處理了。
Redis 提供了 BRPOP 命令。BRPOP 命令也稱為阻塞式讀取,客戶端在沒有讀到隊列數據時,自動阻塞,直到有新的數據寫入隊列,再開始讀取新數據。
缺陷:生產者消息發送很快,而消費者處理消息的速度比較慢,這就導致 List 中的消息越積越多,給 Redis 的內存帶來很大壓力。

2,Redis提供的PubSub,可以支持多個消費者進行消費,生產者發布一條消息,多個消費者同時訂閱消費。
缺點:1,如果任意一個消費者掛了,等恢復過來后,在這期間的生產者的數據就丟失了。PubSub只把數據發給在線的消費者,消費者一旦下線,就會丟棄數據。
2,PubSub中的數據不支持數據持久化,當Redis宕機恢復后,其他類型的數據都可以從RDB和AOF中恢復回來,但PubSub不行,它就是簡單的基于內存的多播機制。

3,Redis 5.0推出了Stream數據結構,它借鑒了Kafka的設計思想,彌補了List和PubSub的不足。Stream類型數據可以持久化、支持ack機制、支持多個消費者、支持回溯消費,基本上實現了隊列中間件大部分功能,比List和PubSub更可靠。
redis實現消息隊列的幾種方式及其優劣

二值狀態統計:Bitmap。這是 Redis 提供的擴展數據類型。
Bitmap 本身是用 String 類型作為底層數據結構實現的一種統計二值狀態的數據類型。String 類型是會保存為二進制的字節數組,所以,Redis 就把字節數組的每個 bit 位利用起來,用來表示一個元素的二值狀態。你可以把 Bitmap 看作是一個 bit 數組。
Bitmap 提供了 GETBIT/SETBIT 操作,使用一個偏移值 offset 對 bit 數組的某一個 bit 位進行讀和寫。不過,需要注意的是,Bitmap 的偏移量是從 0 開始算的,也就是說 offset 的最小值是 0。

假設我們要統計 ID 3000 的用戶在 2020 年 8 月份的簽到情況,就可以按照下面的步驟進行操作。
第一步,執行下面的命令,記錄該用戶 8 月 3 號已簽到。(從0開始,3號offset為2)

SETBIT uid:sign:3000:202008 2 1

第二步,檢查該用戶 8 月 3 日是否簽到。

GETBIT uid:sign:3000:202008 2

第三步,統計該用戶在 8 月份的簽到次數。

BITCOUNT uid:sign:3000:202008

Bitmap 支持用 BITOP 命令對多個 Bitmap 按位做“與”“或”“異或”的操作,操作的結果會保存到一個新的 Bitmap 中。



回到剛剛的問題,在統計 1 億個用戶連續 10 天的簽到情況時,你可以把每天的日期作為 key,每個 key 對應一個 1 億位的 Bitmap,每一個 bit 對應一個用戶當天的簽到情況。
接下來,我們對 10 個 Bitmap 做“與”操作,得到的結果也是一個 Bitmap。在這個 Bitmap 中,只有 10 天都簽到的用戶對應的 bit 位上的值才會是 1。最后,我們可以用 BITCOUNT 統計下 Bitmap 中的 1 的個數,這就是連續簽到 10 天的用戶總數了。
現在,我們可以計算一下記錄了 10 天簽到情況后的內存開銷。每天使用 1 個 1 億位的 Bitmap,大約占 12MB 的內存(10^8/8/1024/1024),10 天的 Bitmap 的內存開銷約為 120MB,內存壓力不算太大。
不過,在實際應用時,最好對 Bitmap 設置過期時間,讓 Redis 自動刪除不再需要的簽到記錄,以節省內存開銷。所以,如果只需要統計數據的二值狀態,例如商品有沒有、用戶在不在等,就可以使用 Bitmap,因為它只用一個 bit 位就能表示 0 或 1。在記錄海量數據時,Bitmap 能夠有效地節省內存空間。

布隆過濾器:底層數據結構可以理解為bitmap,bitmap也可以簡單理解為是一個數組,元素只存儲0和1,所以它占用的空間相對較小,當一個元素要存入bitmap時,其實是要去看存儲到bitmap的哪個位置,這時一般用的就是哈希算法,存進去的位置標記為1,只要使用哈希算法離不開「哈希沖突」,導致有存在「誤判」的情況。所以在布隆過濾器中,如果元素判定為存在,那該元素「未必」真實存在。如果元素判定為不存在,那就肯定是不存在。布隆過濾器也不能「刪除」元素(也是哈希算法的局限性,在布隆過濾器中是不能準確定位一個元素的)http://www.lxweimin.com/p/208c91607fd3

基數統計:HyperLogLog 是一種用于統計基數的數據集合類型,它的最大優勢就在于,當集合元素數量非常多時,它計算基數所需的空間總是固定的,而且還很小。
HyperLogLog 的統計規則是基于概率完成的,所以它給出的統計結果是有一定誤差的,標準誤算率是 0.81%。這也就意味著,你使用 HyperLogLog 統計的 UV 是 100 萬,但實際的 UV 可能是 101 萬。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容