一、引言
學習redis 也有一段時間了,該接觸的也差不多了。後來有一天,以為同僚問我,如何向redis中批量的增加資料,肯定是大批量的,為了這主題,我從新找起了解決方案。目前的解決方案大都是從官網上查找和翻譯的,每個執行個體也都調試了,正确無誤。把結果告訴我同僚的時候,我也跟清楚這個主題如何操作了,裡面的細節也更清楚了。大然也有人說可以通過腳本來做這個操作,沒錯,但是我對腳本語言還沒有研究很透,就不來班門弄斧了。
二、管道的由來
1、請求/響應協定和RTT
Redis是使用 用戶端-伺服器(Client-Server) 模型的TCP伺服器,稱為請求/響應協定。
這意味着通常通過以下步驟完成請求:
1.1、用戶端向伺服器發送查詢,并通常以阻塞的方式從套接字讀取伺服器響應。
1.2、伺服器處理指令并将響應發送回用戶端。
例如,這四個指令序列就是這樣的:
用戶端和伺服器通過網絡鍊路進行連接配接。這樣的連結可以非常快(一個回送接口)或非常慢(通過網際網路在兩台主機之間建立很多跳轉的連接配接)。無論網絡延遲如何,資料包都會從用戶端傳輸到伺服器,然後從伺服器傳回用戶端以進行回複。
這個時間來回被稱為RTT(往返時間)。當用戶端需要連續執行多個請求時(例如,将許多元素添加到同一個清單或使用多個鍵填充資料庫),很容易看到這會很影響性能。例如,如果RTT時間為250毫秒(在網際網路上連接配接速度非常慢的情況下),即使伺服器能夠每秒處理100k個請求,此時我們也隻能夠每秒最多處理四個請求。
如果使用的接口是本地回送接口(loopback),則RTT要短得多(例如,我的主機報告0.0,040毫秒ping 127.0.0.1),但如果您需要連續執行很多寫操作,則仍然需要很多的時間。
幸運的是,有一種方法可以改善這種做法。
2、Redis的管道
請求/響應伺服器可以這樣實作,即使用戶端沒有閱讀上一條指令的回複,它也能夠處理新的請求。通過這種方式,可以發送多個指令到伺服器而無需等待回複,最後一步讀取回複。
這被稱為管道技術,并且是被廣泛使用的技術。例如,許多POP3協定的實作已經支援這個功能,顯著加快了從伺服器下載下傳新電子郵件的過程。
Redis自從早期的版本開始就支援管道的操作,是以無論您運作哪種版本,都可以使用Redis進行管道的操作。這是使用原始netcat實用程式的示例:
(如果執行nc指令,提示:command not found,安裝指令即可,即:yum install nc)
這次我們沒有為每次通話支付RTT的時間成本,隻是把三指令作為了一個指令執行,最後隻為這一次執行花費了時間。
非常明确地說,通過管道的操作,我們第一個例子的操作順序如下:
重要提示:當用戶端使用管道發送多條指令時,伺服器将被迫使用記憶體排隊答複。是以如果你需要使用管道發送大量的指令,最好将這些指令以合理的數目進行分組來批量發送,例如10k指令,讀取回複,然後再發送另一個10k的指令,類似這樣。速度幾乎相同,所使用的額外記憶體的最大量将是将最大限度地排隊此10k指令的回複所需的數量。
3、這不僅僅是RTT的問題
管道不僅僅是為了減少往返時間所帶來的延遲成本,它實際上可以提高您在給定的Redis伺服器上每秒執行的總操作量。這是事實,即在不使用管道的情況下,從通路資料結構和生成答複的角度來看,每個指令的執行成本都不高的,但從執行套接字 I/O 操作的角度來看,這是非常昂貴的。當涉及調用read()和write()調用的時候,這個調用操作意味着要切換操作環境,要從使用者登陸切換到核心登陸。最後來看,其實上下文切換才是導緻速度大幅度的降低的罪魁禍首。
當使用Redis的管道的時候,許多指令通常通過對一個read()函數的系統的調用來讀取,并且通過對一個write()函數的系統的調用來傳遞多個響應。是以,每秒執行的總查詢數量随着管道的操作呈線性增加,并最終達到未使用管道的基線的10倍,如下圖所示:
4、一些真實世界的代碼示例
在以下基準測試中,我們将使用支援管道的Redis Ruby用戶端來測試由于管道而導緻的速度提升:
運作上述簡單腳本将在我的Mac OS X系統中提供以下圖形,通過環回接口運作,其中管道将提供最小的改進,其他保持不變,因為RTT已經非常低:
正如您所看到的,使用管道,我們将傳輸速度改提升五倍。
5、管道VS腳本
使用Redis腳本(Redis版本2.6或更高版本中可用),可以在伺服器端更高效執行處理大量的管道用例的工作。 腳本的一大優點是它能夠以最小的延遲讀取和寫入資料,使得讀取,計算,寫入等操作非常快速(在這種情況下,管道操作不起作用,因為用戶端在調用寫入指令之前需要讀取指令的回複)。
有時,應用程式可能還想在管道中發送EVAL或EVALSHA指令。這是完全可能的,并且Redis通過SCRIPT LOAD指令明确是支援的(它保證可以在沒有失敗風險的情況下調用EVALSHA)。
6、 EVALSHA sha1 numkeys key [key ...] arg [arg ...]
Redis可以使用該指令的版本是2.6.0,或者更高的版本。
時間複雜度:取決于執行的腳本。
通過其SHA1摘要評估緩存在伺服器端的腳本。使用SCRIPT LOAD指令将腳本緩存在伺服器端。該指令在其他方面與EVAL相同。
7、附錄:為什麼即使在回送接口上,一個繁忙的循環也很慢?
即使在本頁面介紹的所有背景下,您仍然可能想知道為什麼如在下所示的Redis基準測試中(在僞代碼中),即使在回送接口中執行,并且伺服器和用戶端在同一實體機器上運作時,也很慢:
畢竟,如果Redis程序和基準測試都在同一個框中運作,那麼這不僅僅是通過記憶體将消息從一個地方複制到另一個地方,而沒有任何實際的延遲和實際網絡?
原因是系統中的程序并不總是在運作,實際上是核心排程器讓程序運作的,是以會發生如下的情況,例如,當基準測試程式被允許運作,從Redis伺服器讀取回複(與最後執行的指令相關),并寫入新的指令。該指令現在位于回送接口緩沖區中,但為了被伺服器讀取,核心排程器應該安排伺服器程序(目前在系統調用中阻塞)運作,等等。 是以,實際上,由于核心排程程式的工作原理,回送接口仍然會有網絡延遲的。
基本上,使用一個繁忙的循環來執行基準測試是一件愚蠢的事情,可以在網絡伺服器中測量性能時完成相關測試。明智的做法是避免以這種方式做基準測試。
三、結束