Redis提供了5種數據結構,但除此以外,Redis還提供了注入慢查詢分析,Redis Shell、Pipeline、事務、與Lua腳本、Bitmaps、HyperLogLog、PubSub、GEO等附加功能,這些功能能夠在某些場景發揮很重要的做用.數據庫
Redis
客戶端執行一條命令分爲如下四個步驟:網絡
1.發送命令
2.命令排隊
3.命令執行
4.返回結果數據結構
其中,第一步+第四步稱爲Round Trip Time
(RTT
,往返時間).併發
Redis
提供了批量操做命令(例如mget
,mset
等),有效的節約RTT
.但大部分命令是不支持批量操做的,例如要執行n
次hgetall
命令,並無mhgetall
存在,須要消耗n
次RTT
.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
性能瓶頸是網絡這樣的說法.性能
可使用Pipeline
模擬出批量操做的效果,可是在使用時須要質疑它與原生批量命令的區別,具體包含幾點:優化
Pipeline
是非原子性的.key
,Pipeline
支持多個命令.Redis
服務端支持實現的,而Pipeline
須要服務端與客戶端的共同實現.Pipeline
雖然好用,可是每次Pipeline
組裝的命令個數不能沒有節制,不然一次組裝Pipeline
數據量過大,一方面會增長客戶端的等待時機,另外一方面會形成必定的網絡阻塞,能夠將一次包含大量命令的Pipeline
拆分紅屢次較小的Pipeline
來完成.網站
Pipeline
只能操做一個Redis
實例,但即便在分佈式Redis
場景中,也能夠做爲批量操做的重要優化方法.spa
爲了保證多條命令組合的原子性,Redis
提供了簡單的事務以及集成Lua
腳原本解決這個問題.
熟悉關係型數據庫的開發者應該對事務比較瞭解,簡單地說,事務表示一組動做,要麼所有成功,要不所有不成功.例如在在電商網站中用戶購買商品A那麼須要將商品A的庫存-1,並建立一個訂單.這兩個操做要麼遠不執行成功,要麼所有執行不成功,不然會出現數據不一致的狀況.
Redis
提供了簡單的功能,將一組須要一塊兒執行的命令放到multi
和exec
兩個命令之間.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
執行後,用戶購買商品的行爲纔算完成,以下兩個結果對應hincrby
和rpush
命令.
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
,屬於語法錯誤,會形成整個事務沒法執行,key
和counter
的值未發生變化:
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
命令已經執行成功,開發者須要本身修改這類問題.