1. 程式人生 > 實用技巧 >Redis事務、PubSub、小物件壓縮

Redis事務、PubSub、小物件壓縮

同舟共濟一一事務

Redis 事務的基本用法

multi 指示事務的開始, exec 指示事務的執行, discard 指示事務的丟棄.

所有的指令在 exec 之前不執行,而是快取在伺服器的一個事務佇列中,伺服器一旦收到 exec 指令,才開始執行整個事務佇列,執行完畢後一次性返回所有指令的執行結果。因為 Redis 的單執行緒特性,它不用擔心自己在執行佇列的時被其他執行緒搶佔。

原子性

事務的原子性是指事務要麼全部成功, 要麼全部失敗,而 Redis 的事務根本不具備“原子性 ,而僅僅是滿足了事務的“隔離性”中的序列化——當前執行的事務有著不被其他事務打斷的權利。

discard (丟棄)

​ 用於丟棄事務快取佇列中的所有指令,在exec 執行之前。

優化

​ Redis 事務在傳送每個指令到事務快取佇列時都要經過一次網路讀寫, 當一個事務內部的指令較多時,需要的網路 IO 時間也會線性增長, 所以通常Redis的客戶端在執行事務時都會結合 pipeline 一起使用,這樣可以將多次 IO 操作壓縮為單次 IO 操作。

watch

​ Redis 儲存了我們的賬戶餘額資料,它是一個整數。現在有兩個併發的客戶端要對賬戶餘額進行修改操作,要對餘額乘以個倍數。可以通過 Redis 的分散式鎖來避免併發問題衝突,分散式鎖是一種悲觀鎖,Redis 提供了這種 watch 的機制,它就是一種樂觀鎖。

​ watch 會在開啟事務之前盯住一個或多個關鍵變數,當事務執行時,伺服器收到了 exec 指令順序執行快取的事務佇列時, Redis 會檢查關鍵變數自 watch 之後是否被修改了。如果關鍵變數被人改了, exec 指令就會返回 NULL 回覆告知客戶端事務執行失敗,客戶端一般會選擇重試。

注意事項

Redis 禁止在 multi 和 exec 之間執行 watch 指令,而必須在 multi 之前盯住關鍵變數,否則會出錯。

小道訊息——PubSub

訊息多播

​ 訊息多播允許生產者只生產一次訊息,由中介軟體負責將訊息複製到多個訊息佇列,每個訊息佇列由相應的消費組進行消費,是分散式系統常用的一種解耦方式,用於將多個消費組的邏輯進行拆分

。支援了訊息多播,多個消費組的邏輯就可以放到不同的子系中。如果是普通的訊息佇列,就得將多個不同的消費組邏輯串接起來放在一個子系統中,進行連續消費。

PubSub

使用了PubSub模組來支援訊息多播,全稱是PublisherSubscriber(釋出者/訂閱者模式)。

PubSub 的消費者如果使用休眠的方式來輪詢訊息,也會遭遇訊息處理不及時的問題。不過我們可以使用 listen 阻塞監聽訊息來進行處理,這點同 blpop 原理是一樣的。

模式訂閱

消費者訂閱一個主題是必須明確指定主題的名稱。如果我們想要訂閱多個主題,那就 subscribe 多個名稱。

subscribe key.a key.b key.c #同時訂閱三個主題, 會有三條訂閱成功反饋資訊

publish key.a message

Redis 提供了模式訂閱功能 Pattern Subscribe ,可以一次訂閱多個主題,即使生產者新增加了同模式的主題,消費者可以立即收到訊息。

psubscribe codehole . * #用模式匹配一次訂閱多個主題,主題以 codehole 字元開頭的訊息都可以收到

訊息結構

{’ pattern’ : None, ’ type ’:’subscribe ’,’ channel ’:’ codehole ’,’data ’: lL}

{’ pattern ’ : None, ’type ’:’message ’,'channel ’ : ’ codehole ’,’data ’ : java comes }

1 . data是訊息的內容, 一個字串。 2. channel 它表示當前訂閱的主題名稱。 3. type 表示訊息的型別。如果是一個普通的訊息,那麼型別就是 message; 如果是控制訊息,比如訂閱指令的反饋,它的型別就是 subscribe :如果是模式訂閱的反饋,它的型別就是 psubscribe ;此外還有取消訂閱指令的反饋 unsubscribe 和 punsubscribe。4. pattern 表示當前訊息是使用哪種模式訂閱到的。如果是通過 subscribe 指令訂閱的,那麼這個欄位就是空。

PubSub 的缺點

如果 Redis 停機重啟, PubSub 的訊息是不會持久化的,Redis 宕機就相當於一個消費者都沒有,所有的訊息會被直接丟棄。因此幾乎找不到合適的應用場景。

開源節流一一小物件壓縮

32bit VS 64bit

Redis 如果使用 32bit 進行編譯,內部所有資料結構所使用的指標空間佔用會少 一半,如果你的 Redis 使用記憶體不超過 4GB ,可以考慮使用 32bit 進行編譯,能夠節約大量記憶體。

小物件壓縮儲存(ziplist)

​ 如果 Redis 內部管理的集合資料結構很小,它會使用緊湊儲存形式壓縮儲存。使用一維陣列進行儲存,需要查詢時,因為元素少,進行遍歷也很快,甚至可以比 HashMap 本身的查詢還要快。Redis的 ziplist 個緊湊的位元組陣列結構。如果它儲存的是 hash 結構,那麼 key 和 value 會作為兩個 entry被相鄰儲存。如果它儲存的是 zset 結構,那麼 value 和score 會作為兩個 entry被相鄰儲存。

Redis 的 intset 是一個緊湊的整數陣列結構,Redis 支援 set 集合動態從 uint16升級到 uint32 ,再升級到 uint64。如果 set 裡儲存的是字串,那麼 sadd 立即升級為 hashtable 結構。

Redis 規定小物件儲存結構的限制條件

hash max-ziplist-entries 512  # hash 的元素個數超過 512 就必須用標準
結構儲存
hash- max-ziplist-value 64  # hash 的任意元素的 key/value 的長度超過 64 就必須用標準結構儲存
list-max-ziplist-entries 512  # list 的元素個數超過 512 就必須用標準結構儲存
list-max-ziplist value 64     # list 的任意元素的長度超過 64 就必須用標準結構儲存
zset-max-ziplist-entries 128  # zset 的元素個數超過 128 就必須用標準結構儲存
zset-max- ziplist-value 64  # zset 的任意元素的長度超過 64 就必須用標準結構儲存
set-max-intset-entries 512  # set 的整數元素個數超過 512 就必須用標準結構儲存

記憶體回收機制

​ Redis 不一定將空閒記憶體立即歸還給作業系統。 如果當前 Redis 記憶體有10GB ,當你刪除了1GB 的key 後,再去觀察記憶體發現變化不會太大。原因是作業系統是以頁為單位來回收記憶體的,這個頁上只要還有一個 key 在使用,那麼它就不能被回收。 Redis 雖然刪除了 1GB 的key ,但 這些 key 分散到了很多頁面中,每個頁面都還有其他 key 存在,這就導致了記憶體不會被立即回收。 如果你執行 flushdb 然後再觀察記憶體,會發現記憶體確實被回收了 。因為所有的 key 都被幹掉了,大部分之前使用的頁面都完全乾淨了,就會立即被作業系統回收。 Redis 然無法保證立即回收已經刪除的 key 的記憶體,但是它會重新使用那些尚未回收的空閒記憶體。

記憶體分配演算法

Redis將記憶體分配的細節丟給了第三方記憶體分配庫去實現。目前 Redis 可以使用 jemalloc ( facebook) 庫採管理記憶體,也可以切換到 tcmalloc ( google )庫。Redis 預設使用了 jemalloc,因為jemalloc 的效能相比 tcmalloc 要好。使用info memory 指令可以看到 Redis的 mem_allocator 使用了 jemalloc。