redis學習筆記 - Pipeline與事務


Redis提供了5種數據結構,但除此之外,Redis還提供了注入慢查詢分析,Redis Shell、Pipeline、事務、與Lua腳本、Bitmaps、HyperLogLog、PubSub、GEO等附加功能,這些功能可以在某些場景發揮很重要的作用.

 https://segmentfault.com/a/1190000011440752

Pipeline

1. Pipeline概念

Redis客戶端執行一條命令分為以下四個步驟:

1.發送命令
2.命令排隊
3.命令執行
4.返回結果

其中,第一步+第四步稱為Round Trip Time(RTT,往返時間).

圖1.1 沒有Pipeline執行n次命令

Redis提供了批量操作命令(例如mget,mset等),有效的節約RTT.但大部分命令是不支持批量操作的,例如要執行nhgetall命令,並沒有mhgetall存在,需要消耗nRTT.Redis的客戶端和服務端可能不是在不同的機器上.例如客戶端在北京,Redis服務端在上海,兩地直線距離為1300公里,那么1次RTT時間=1300×2/(300000×2/3)=13毫秒(光在真空中傳輸速度為每秒30萬公里,這里假設光纖的速度為光速的2/3),那么客戶端在1秒內大約只能執行80次左右的命令,這個和Redis的高並發高吞吐背道而馳.

Pipeline(流水線)機制能改善上面這類問題,它能將一組Redis命令進行組裝,通過一次RTT傳輸給Redis,再將這組Redis命令按照順序執行並裝填結果返回給客戶端.圖1.1中未使用Pipeline執行了n次命令,整個過程需要n個RTT.

Pipeline並不是什么新的技術和機制,很多技術上都使用過.而且RTT在不同網絡環境下會有不同,例如同機房和同機器會比較快,跨機房跨地區會比較慢.Redis命令真正執行的時間通常在微秒級別,所以才會有Redis性能瓶頸是網絡這樣的說法.

2. 原生批量命令與Pipeline對比

可以使用Pipeline模擬出批量操作的效果,但是在使用時需要質疑它與原生批量命令的區別,具體包含幾點:

  • 原生批量命令是原子性,Pipeline是非原子性的.
  • 原生批量命令是一個命令對應多個key,Pipeline支持多個命令.
  • 原生批量命令是Redis服務端支持實現的,而Pipeline需要服務端與客戶端的共同實現.

3. Pipeline總結

Pipeline雖然好用,但是每次Pipeline組裝的命令個數不能沒有節制,否則一次組裝Pipeline數據量過大,一方面會增加客戶端的等待時機,另一方面會造成一定的網絡阻塞,可以將一次包含大量命令的Pipeline拆分成多次較小的Pipeline來完成.

Pipeline只能操作一個Redis實例,但即使在分布式Redis場景中,也可以作為批量操作的重要優化方法.

事務

為了保證多條命令組合的原子性,Redis提供了簡單的事務以及集成Lua腳本來解決這個問題.

熟悉關系型數據庫的開發者應該對事務比較了解,簡單地說,事務表示一組動作,要么全部成功,要不全部不成功.例如在在電商網站中用戶購買商品A那么需要將商品A的庫存-1,並創建一個訂單.這兩個操作要么遠不執行成功,要么全部執行不成功,否則會出現數據不一致的情況.

Redis提供了簡單的功能,將一組需要一起執行的命令放到multiexec兩個命令之間.multi命令代表事務的開始,exec命令代表事務結束,他們之間的命令是原子順序執行的.
例如上述的用戶購買商品問題:

    127.0.0.1:6379> multi OK 127.0.0.1:6379> hincrby commodity:a:detail stock -1 QUEUE 127.0.0.1:6379> rpush user:1:orders {"commodity":'a',..} QUEUE

可以看到數據操作命令返回的結果是QUEUE,代表命令並沒有真正執行,而是暫時保存在Redis中.如果此時另一個客戶端執行llen user:1:orders返回結果為0.

    127.0.0.1:6379> llen user:1:orders (integer) 0

只有當exec執行后,用戶購買商品的行為才算完成,如下兩個結果對應hincrbyrpush命令.

    127.0.0.1:6379> exec 1) (integer) 4 # 商品原庫存為5 2) (integer) 1 127.0.0.1:6379> llen user:1:orders (integer) 1

如果要停止事務的執行,可以使用discard命令替代exec命令即可.

    127.0.0.1:6379> discard OK 127.0.0.1:6379> llen user:1:orders (integer) 0

如果事務中的命令出現錯誤,Redis的處理機制也不盡相同.

1.命令錯誤
例如下面操作錯將set寫成了sett,屬於語法錯誤,會造成整個事務無法執行,keycounter的值未發生變化:

    127.0.0.1:6379> mget key counter
    1) "hello" 2) "100" 127.0.0.1:6379> multi OK 127.0.0.1:6379> sett key world (error) ERR unknown command 'sett' 127.0.0.1:6379> incr counter QUEUE 127.0.0.1:6379> exec (error) EXECABORT Transaction discarded because of previous errors. 127.0.0.1:6379> mget key counter 1) "hello" 2) "100"

2.運行時錯誤
例如用戶購買商品,誤把rpush寫成了zadd

    127.0.0.1:6379> multi OK 127.0.0.1:6379> hincrby commodity:a:detail stock -1 QUEUED 127.0.0.1:6379> zadd user:1:orders {"commodity":'a',..} QUEUED 127.0.0.1:6379> exec 1) (integer) 1 2) (error) WRONGTYPE Operation against a key holding the wrong kind of value. 127.0.0.1:6379> hget commodity:a:detail stack (integer) 3

可以看到Redis並不支持回滾功能,hincrby commodity:a:detail stock -1命令已經執行成功,開發者需要自己修改這類問題.


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM