Redis響應延時問題排查

{"type":"doc","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"link","attrs":{"href":"https://www.oschina.net/translate/redis-latency-problems-troubleshooting?lang=chs&p=4","title":"","type":null},"content":[{"type":"text","text":"原文","attrs":{}}]},{"type":"text","text":" ","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"本文將有助於你找出Redis 響應延遲的問題所在。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"文中出現的延遲(latency)均指從客戶端發出一條命令到客戶端接受到該命令的反饋所用的最長響應時間。Reids通常處理(命令的)時間非常的慢,大概在次微妙範圍內,但也有更長的情況出現。","attrs":{}}]},{"type":"horizontalrule","attrs":{}},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"計算延遲時間","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"如果你正在經歷響應延遲問題,你或許能夠根據應用程序的具體情況算出它的延遲響應時間,或者你的延遲問題非常明顯,宏觀看來,一目瞭然。不管怎樣吧,用redis-cli可以算出一臺Redis 服務器的到底延遲了多少毫秒。踹這句:","attrs":{}}]},{"type":"codeblock","attrs":{"lang":"shell"},"content":[{"type":"text","text":"redis-cli --latency -h `host` -p `port`","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"網絡和通信引起的延遲","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"當用戶連接到Redis通過TCP/IP連接或Unix域連接,千兆網絡的典型延遲大概200us,而Unix域socket可能低到30us。這完全基於你的網絡和系統硬件。在通信本身之上,系統增加了更多的延遲(線程調度,CPU緩存,NUMA替換等等)。系統引起的延遲在虛擬機環境遠遠高於在物理機器環境。","attrs":{}}]},{"type":"horizontalrule","attrs":{}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"實際情況是即使Redis處理大多數命令在微秒之下,客戶機和服務器之間的交互也必然消耗系統相關的延遲。一個高效的客戶機因而試圖通過捆綁多個命令在一起的方式減少交互的次數。服務器和大多數客戶機支持這種方式。聚合命令象MSET/MGET也可以用作這個目的。從Redis 2.4版本起,很多命令對於所有的數據類型也支持可變參數。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"這裏有一些指導:","attrs":{}}]},{"type":"bulletedlist","content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"如果你負擔的起,儘可能的使用物理機而不是虛擬機來做服務器","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"不要經常的connect/disconnect與服務器的連接(尤其是對基於web的應用),儘可能的延長與服務器連接的時間。","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"如果你的客戶端和服務器在同一臺主機上,則使用Unix域套接字","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"儘量使用聚合命令(MSET/MGET)或可變參數命令而不是pipelining","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"如果可以儘量使用pipelining而不是序列的往返命令。","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"針對不適合使用原始pipelining的情況,如某個命令的結果是後續命令的輸入,在以後的版本中redis提供了對服務器端的lua腳本的支持,實驗分支版本現在已經可以使用了。","attrs":{}}]}]}],"attrs":{}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在Linux上,你可以通過process placement(taskset)、cgroups、real-time priorities(chrt)、NUMA配置(numactl)或使用低延遲內核的方式來獲取較低的延遲。請注意","attrs":{}},{"type":"text","marks":[{"type":"italic","attrs":{}}],"text":"Redis","attrs":{}},{"type":"text","text":" 並不適合被綁到","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"單個CPU核","attrs":{}},{"type":"text","text":"上。redis會在後臺創建一些非常消耗CPU的進程,如bgsave和AOF重寫,這些任務是","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"絕對不能","attrs":{}},{"type":"text","text":"和主事件循環進程放在一個CPU核上的。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"大多數情況下上述的優化方法是不需要的,除非你確實需要並且你對優化方法很熟悉的情況下再使用上述方法。","attrs":{}}]},{"type":"horizontalrule","attrs":{}},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"Redis的單線程屬性","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"Redis 使用了單線程的設計, 意味着單線程服務於所有的客戶端請求,使用一種複用的技術。這種情況下redis可以在任何時候處理單個請求, 所以所有的請求是順序處理的。這和Node.js的工作方式很像, 所有的產出通常不會有慢的感覺,因爲處理單個請求的時間非常短,但是最重要的是這些產品被設計爲非阻塞系統調用,比如從套接字中讀取或寫入數據。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"我提到過Redis從2.4版本後幾乎是單線程的,我們使用線程在後臺運行一些效率低下的I/O操作, 主要關係到硬盤I/O,但是這不改變Redis使用單線程處理所有請求的事實。","attrs":{}}]},{"type":"horizontalrule","attrs":{}},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"低效操作產生的延遲","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"單線程的一個結果是,當一個請求執行得很慢,其他的客戶端調用就必須等待這個請求執行完畢。當執行","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"GET","attrs":{}},{"type":"text","text":"、","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"SET","attrs":{}},{"type":"text","text":"或者 ","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"LPUSH ","attrs":{}},{"type":"text","text":"命令的時候這不是個問題,因爲這些操作可在很短的常數時間內完成。然而,對於多個元素的操作,像","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"SORT","attrs":{}},{"type":"text","text":", ","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"LREM","attrs":{}},{"type":"text","text":", ","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"SUNION","attrs":{}},{"type":"text","text":" 這些,做兩個大數據集的交叉要花掉很長的時間。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"文檔中提到了所有操作的算法複雜性。 在使用一個你不熟悉的命令之前系統的檢查它會是一個好辦法。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"如果你對延遲有要求,那麼就不要執行涉及多個元素的慢操作,你可以使用Redis的replication功能,把這類慢操作全都放到replica上執行。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"可以用Redis 的","attrs":{}},{"type":"link","attrs":{"href":"http://redis.io/commands/slowlog","title":null,"type":null},"content":[{"type":"text","text":"Slow Log ","attrs":{}}]},{"type":"text","text":"來監控慢操作。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"此外,你可以用你喜歡的進程監控程序(top, htop, prstat, 等...)來快速查看Redis進程的CPU使用率。如果traffic不高而CPU佔用很高,八成說明有慢操作。","attrs":{}}]},{"type":"horizontalrule","attrs":{}},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"延遲由fork產生","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"Redis不論是爲了在後臺生成一個RDB文件,還是爲了當AOF持久化方案被開啓時重寫Append Only文件,都會在後臺fork出一個進程。fork操作(在主線程中被執行)本身會引發延遲。在大多數的類unix操作系統中,fork是一個很消耗的操作,因爲它牽涉到複製很多與進程相關的對象。而這對於分頁表與虛擬內存機制關聯的系統尤爲明顯","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"對於運行在一個linux/AMD64系統上的實例來說,內存會按照每頁4KB的大小分頁。爲了實現虛擬地址到物理地址的轉換,每一個進程將會存儲一個分頁表(樹狀形式表現),分頁表將至少包含一個指向該進程地址空間的指針。所以一個空間大小爲24GB的redis實例,需要的分頁表大小爲  24GB/4KB*8 = 48MB。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"當一個後臺的save命令執行時,實例會啓動新的線程去申請和拷貝48MB的內存空間。這將消耗一些時間和CPU資源,尤其是在虛擬機上申請和初始化大塊內存空間時,消耗更加明顯。","attrs":{}}]},{"type":"horizontalrule","attrs":{}},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"在不同系統中的Fork時間","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"除了Xen系統外,現代的硬件都可以快速完美的複製頁表。Xen系統的問題不是特定的虛擬化,而是特定的Xen.例如使用VMware或者Virutal Box不會導致較慢的fork時間。下面的列表比較了不同Redis實例的fork時間。數據包含正在執行的BGSAVE,並通過INFO指令查看thelatest_fork_usecfiled。","attrs":{}}]},{"type":"bulletedlist","content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"Linux beefy VM on VMware","attrs":{}},{"type":"text","text":" 6.0GB RSS forked 77 微秒 (每GB 12.8 微秒 ).","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"Linux running on physical machine (Unknown HW)","attrs":{}},{"type":"text","text":" 6.1GB RSS forked 80 微秒(每GB 13.1微秒)","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"Linux running on physical machine (Xeon @ 2.27Ghz)","attrs":{}},{"type":"text","text":" 6.9GB RSS forked into 62 微秒 (每GB 9 微秒).","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"Linux VM on 6sync (KVM)","attrs":{}},{"type":"text","text":" 360 MB RSS forked in 8.2 微秒 (每GB 23.3 微秒).","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"Linux VM on EC2 (Xen)","attrs":{}},{"type":"text","text":" 6.1GB RSS forked in 1460 微秒 (每GB 239.3 微秒).","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"Linux VM on Linode (Xen)","attrs":{}},{"type":"text","text":" 0.9GBRSS forked into 382 微秒 (每GB 424 微秒).","attrs":{}}]}]}],"attrs":{}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"你能看到運行在Xen上的VM的Redis性能相差了一到兩個數量級。我們相信這是Xen系統的一個驗證問題,我們希望這個問題能儘快處理。","attrs":{}}]},{"type":"horizontalrule","attrs":{}},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"swapping (操作系統分頁)引起的延遲","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"Linux (以及其他一些操作系統) 可以把內存頁存儲在硬盤上,反之也能將存儲在硬盤上的內存頁再加載進內存,這種機制使得內存能夠得到更有效的利用。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"如果內存頁被系統移到了swap文件裏,而這個內存頁中的數據恰好又被redis用到了(例如要訪問某個存儲在內存頁中的key),系統就會暫停redis進程直到把需要的頁數據重新加載進內存。這個操作因爲牽涉到隨機I/O,所以很慢,會導致無法預料的延遲。","attrs":{}}]},{"type":"horizontalrule","attrs":{}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"系統之所以要在內存和硬盤之間置換redis頁數據主要因爲以下三個原因:","attrs":{}}]},{"type":"bulletedlist","content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"系統總是要應對內存不足的壓力,因爲每個運行的進程都想申請更多的物理內存,而這些申請的內存的數量往往超過了實際擁有的內存。簡單來說就是redis使用的內存總是比可用的內存數量更多。","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"redis實例的數據,或者部分數據可能就不會被客戶端訪問,所以系統可以把這部分閒置的數據置換到硬盤上。需要把所有數據都保存在內存中的情況是非常罕見的。","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"一些進程會產生大量的讀寫I/O。因爲文件通常都有緩存,這往往會導致文件緩存不斷增加,然後產生交換(swap)。請注意,redis RDB和AOF後臺線程都會產生大量文件。","attrs":{}}]}]}],"attrs":{}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"所幸Linux提供了很好的工具來診斷這個問題,所以當延遲疑似是swap引起的,最簡單的辦法就是使用Linux提供的工具去確診。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"首先要做的是檢查swap到硬盤上的redis內存的數量,爲實現這個目的要知道redis實例的進程id:","attrs":{}}]},{"type":"codeblock","attrs":{"lang":"shell"},"content":[{"type":"text","text":"$ redis-cli info | grep process_id\nprocess_id:5454","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"進入進程目錄:","attrs":{}}]},{"type":"codeblock","attrs":{"lang":"shell"},"content":[{"type":"text","text":"$ cd /proc/5454","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在這裏你會發現一個名爲","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"smaps","attrs":{}},{"type":"text","text":" 的文件,它描述了redis進程的內存佈局 (假定你使用的是Linux 2.6.16或者更新的版本)。這個文件包括了很多進程所使用內存的細節信息,其中有一項叫做","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"Swap","attrs":{}},{"type":"text","text":"的正是我們所關心的。不過僅看這一項是不夠的,因爲smaps文件包括有redis進程的多個不同的的內存映射區域的使用情況(進程的內存佈局遠不是線性排列那麼簡單)。","attrs":{}}]},{"type":"horizontalrule","attrs":{}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"從我們對所有進程的內存交換情況感興趣以來,我們首先要做的事情是使用grep命令顯示進程的smaps文件","attrs":{}}]},{"type":"codeblock","attrs":{"lang":"shell"},"content":[{"type":"text","text":"$ cat smaps | grep 'Swap:'\nSwap: 0 kB\nSwap: 0 kB\nSwap: 0 kB\nSwap: 0 kB\nSwap: 0 kB\nSwap: 12 kB\nSwap: 156 kB\nSwap: 8 kB\nSwap: 0 kB\nSwap: 0 kB\nSwap: 0 kB\nSwap: 0 kB\nSwap: 0 kB\nSwap: 0 kB\nSwap: 0 kB\nSwap: 0 kB\nSwap: 0 kB\nSwap: 4 kB\nSwap: 0 kB\nSwap: 0 kB\nSwap: 4 kB\nSwap: 0 kB\nSwap: 0 kB\nSwap: 4 kB\nSwap: 4 kB\nSwap: 0 kB\nSwap: 0 kB\nSwap: 0 kB\nSwap: 0 kB\nSwap: 0 kB","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"假如所有的數據顯示爲0kb或者某些數據偶爾顯示爲4kb,表示當前一切正常。實際上我們的例子是一個真實的運行着Redis並每秒爲數百的用戶提供服務的網站,會顯示更多的交換頁。爲了研究是否存在一個嚴重的問題,我們改變命令打印出分配的內存尺寸","attrs":{}}]},{"type":"codeblock","attrs":{"lang":"shell"},"content":[{"type":"text","text":"$ cat smaps | egrep '^(Swap|Size)'\nSize: 316 kB\nSwap: 0 kB\nSize: 4 kB\nSwap: 0 kB\nSize: 8 kB\nSwap: 0 kB\nSize: 40 kB\nSwap: 0 kB\nSize: 132 kB\nSwap: 0 kB\nSize: 720896 kB\nSwap: 12 kB\nSize: 4096 kB\nSwap: 156 kB\nSize: 4096 kB\nSwap: 8 kB\nSize: 4096 kB\nSwap: 0 kB\nSize: 4 kB\nSwap: 0 kB\nSize: 1272 kB\nSwap: 0 kB\nSize: 8 kB\nSwap: 0 kB\nSize: 4 kB\nSwap: 0 kB\nSize: 16 kB\nSwap: 0 kB\nSize: 84 kB\nSwap: 0 kB\nSize: 4 kB\nSwap: 0 kB\nSize: 4 kB\nSwap: 0 kB\nSize: 8 kB\nSwap: 4 kB\nSize: 8 kB\nSwap: 0 kB\nSize: 4 kB\nSwap: 0 kB\nSize: 4 kB\nSwap: 4 kB\nSize: 144 kB\nSwap: 0 kB\nSize: 4 kB\nSwap: 0 kB\nSize: 4 kB\nSwap: 4 kB\nSize: 12 kB\nSwap: 4 kB\nSize: 108 kB\nSwap: 0 kB\nSize: 4 kB\nSwap: 0 kB\nSize: 4 kB\nSwap: 0 kB\nSize: 272 kB\nSwap: 0 kB\nSize: 4 kB\nSwap: 0 kB","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在輸出信息中,你能看到有一個720896kb的內存分配(有12kb的交換)還有一個156kb的交換是另一個進程的。基本上我們的內存只會有很小的內存交換,因此不會產生任何的問題","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"假如進程的內存有相當部分花在了swap上,那麼你的延遲可能就與swap有關。假如redis出現這種情況那麼可以用 ","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"vmstat","attrs":{}},{"type":"text","text":" 命令來驗證一下猜測:","attrs":{}}]},{"type":"codeblock","attrs":{"lang":"shell"},"content":[{"type":"text","text":"$ vmstat 1\nprocs -----------memory---------- ---swap-- -----io---- -system-- ----cpu----\n r b swpd free buff cache si so bi bo in cs us sy id wa\n 0 0 3980 697932 147180 1406456 0 0 2 2 2 0 4 4 91 0\n 0 0 3980 697428 147180 1406580 0 0 0 0 19088 16104 9 6 84 0\n 0 0 3980 697296 147180 1406616 0 0 0 28 18936 16193 7 6 87 0\n 0 0 3980 697048 147180 1406640 0 0 0 0 18613 15987 6 6 88 0\n 2 0 3980 696924 147180 1406656 0 0 0 0 18744 16299 6 5 88 0\n 0 0 3980 697048 147180 1406688 0 0 0 4 18520 15974 6 6 88 0","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"輸出中我們最感興趣的兩行是","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"si","attrs":{}},{"type":"text","text":" 和","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":" so","attrs":{}},{"type":"text","text":",這兩行分別統計了從swap文件恢復到內存的數量和swap到文件的內存數量。如果在這兩行發現了非0值那麼就說明系統正在進行swap。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"最後,可以用iostat命令來查看系統的全局I/O行爲。","attrs":{}}]},{"type":"codeblock","attrs":{"lang":"shell"},"content":[{"type":"text","text":"$ iostat -xk 1\navg-cpu: %user %nice %system %iowait %steal %idle\n 13.55 0.04 2.92 0.53 0.00 82.95\n\nDevice: rrqm/s wrqm/s r/s w/s rkB/s wkB/s avgrq-sz avgqu-sz await svctm %util\nsda 0.77 0.00 0.01 0.00 0.40 0.00 73.65 0.00 3.62 2.58 0.00\nsdb 1.27 4.75 0.82 3.54 38.00 32.32 32.19 0.11 24.80 4.24 1.85","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"如果確認延遲是由於swap引起的,那麼就需要減小系統的內存壓力,要麼給機器增加內存,要麼不要在同一個機器上運行其他消耗內存的程序。","attrs":{}}]},{"type":"horizontalrule","attrs":{}},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"AOF 和硬盤I/O操作延遲","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"另一個延遲的根源是Redis的AOF(僅附加文件)模式。AOF基本上是通過兩個系統間的調用來完成工作的。 一個是寫,用來寫數據到AOF, 另外一個是文件數據同步,通過清除硬盤上空核心文件的緩衝來保證用戶指定的持久級別。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"包括寫和文件數據同步的調用都可以導致延遲的根源。 寫實例可以阻塞系統範圍的同步操作,也可以阻塞當輸出的緩衝區滿並且內核需要清空到硬盤來接受新的寫入的操作。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"文件數據同步對於延遲的影響非常大,因爲它涉及到好幾步調用,可能要花掉幾毫秒以致幾秒的時間,特別是在還有其他進程後也在佔用I/O的情況下。因爲這個原因,從redis2.4開始用一個單獨的線程來做文件數據同步。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"我們來看看當使用AOF的時候如何配置來降低延遲。","attrs":{}}]},{"type":"horizontalrule","attrs":{}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"通過設置AOF相關的","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"appendfsync","attrs":{}},{"type":"text","text":"項,可以使用三種不同的方式來執行文件同步(也可以在運行時使用","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"CONFIG SET","attrs":{}},{"type":"text","text":" 命令來修改這個配置)。","attrs":{}}]},{"type":"bulletedlist","content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"appendfsync 的值設置爲","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"no","attrs":{}},{"type":"text","text":",redis不執行fsync。這種情況下造成延遲的唯一原因就是寫操作。這種延遲沒有辦法可以解決,因爲redis接收到數據的速度是不可控的,不過這種情況也不常見,除非有其他的進程佔用I/O使得硬盤速度突然下降。","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"appendfsync 的值設置爲","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"everysec","attrs":{}},{"type":"text","text":",每秒都會執行fsync。fsync 由一個單獨線程執行,如果需要寫操作的時候有fsync正在執行redis就會用一個buffer來延遲寫入2秒(因爲在Linux如果一個fsync 正在運行那麼對該文件的寫操作就會被堵塞)。如果fsync 耗時過長(譯者注:超過了2秒),即使fsync 還在進行redis也會執行寫操作,這就會造成延遲。","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"appendfsync 的值設置爲","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"always ","attrs":{}},{"type":"text","text":",fsync 會在每次寫操作返回成功代碼之前執行(事實上redis會積累多個命令在一次fsync 過程中執行)。這種模式下的性能表現是非常差勁的,所以最好使用一個快速的磁盤和文件系統以加快fsync 的執行。","attrs":{}}]}]}],"attrs":{}},{"type":"horizontalrule","attrs":{}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"大多數redis用戶都會把這個值設成 ","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"no","attrs":{}},{"type":"text","text":" 或者 ","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"everysec","attrs":{}},{"type":"text","text":"。要減少延遲,最好避免在同一個機器上有其他耗費I/O的程序。用SSD也有益於降低延遲,不過即使不使用SSD,如果能有冗餘的硬盤專用於AOF也會減少尋址時間,從而降低延遲。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"如果你想診斷AOF相關的延遲原因可以使用","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"strace ","attrs":{}},{"type":"text","text":"命令:","attrs":{}}]},{"type":"codeblock","attrs":{"lang":null},"content":[{"type":"text","text":"sudo strace -p $(pidof redis-server) -T -e trace=fdatasync","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":" ","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"上面的命令會展示redis主線程裏所有的fdatasync系統調用。不包括後臺線程執行的fdatasync 調用。如果appendfsync 配置爲","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"everysec","attrs":{}},{"type":"text","text":",則給","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"strace","attrs":{}},{"type":"text","text":"增加","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"-f","attrs":{}},{"type":"text","text":"選項。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"用下面命令可以看到fdatasync和write調用:","attrs":{}}]},{"type":"codeblock","attrs":{"lang":"shell"},"content":[{"type":"text","text":"sudo strace -p $(pidof redis-server) -T -e trace=fdatasync,write","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"不過因爲write也會向客戶端寫數據,所以用上面的命令很可能會獲得許多與磁盤I/O沒有關係的結果。似乎沒有辦法讓strace 只顯示慢系統調用,所以要用下面的命令:","attrs":{}}]},{"type":"codeblock","attrs":{"lang":"shell"},"content":[{"type":"text","text":"sudo strace -f -p $(pidof redis-server) -T -e trace=fdatasync,write 2>&1 | grep -v '0.0' | grep -v unfinished","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"horizontalrule","attrs":{}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"數據過期造成的延遲","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"redis有兩種方式來去除過期的key:","attrs":{}}]},{"type":"bulletedlist","content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"italic","attrs":{}}],"text":"lazy","attrs":{}},{"type":"text","text":" 方式,在key被請求的時候才檢查是否過期。 to be already expired.","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"italic","attrs":{}}],"text":"active","attrs":{}},{"type":"text","text":" 方式,每0.1秒進行一次過期檢查。","attrs":{}}]}]}],"attrs":{}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"active過期模式是自適應的,每過100毫秒開始一次過期檢查(每秒10次),每次作如下操作:","attrs":{}}]},{"type":"bulletedlist","content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"根據 REDIS_EXPIRELOOKUPS_PER_CRON 的值去除已經過期的key(是指如果過期的key數量超過了REDIS_EXPIRELOOKUPS_PER_CRON 的值纔會啓動過期操作,太少就不必了。這個值默認爲10), evicting all the keys already expired.","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"假如超過25%(是指REDIS_EXPIRELOOKUPS_PER_CRON這個值的25%,這個值默認爲10,譯者注)的key已經過期,則重複一遍檢查失效的過程。","attrs":{}}]}]}],"attrs":{}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"REDIS_EXPIRELOOKUPS_PER_CRON 默認爲10, 過期檢查一秒會執行10次,通常在actively模式下1秒能處理100個key。在過期的key有一段時間沒被訪問的情況下這個清理速度已經足夠了,所以 ","attrs":{}},{"type":"text","marks":[{"type":"italic","attrs":{}}],"text":"lazy","attrs":{}},{"type":"text","text":"模式基本上沒什麼用。1秒只過期100個key也不會對redis造成多大的影響。","attrs":{}}]},{"type":"horizontalrule","attrs":{}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"這種算法式是自適應的,如果發現有超過指定數量25%的key已經過期就會循環執行。這個過程每秒會運行10次,這意味着隨機樣本中超過25%的key會在1秒內過期。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"通常來講如果有很多key在同一秒過期,超過了所有key的25%,redis就會阻塞直到過期key的比例下降到25%以下。","attrs":{}}]},{"type":"horizontalrule","attrs":{}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"使用這種策略是爲了避免清除過期key的過程佔用太多內存,這種方法對系統幾乎不會有不良影響,因爲大量key同時到期並非是一種常見現象,不過如果用戶使用了 ","attrs":{}},{"type":"link","attrs":{"href":"http://redis.io/commands/expireat","title":null,"type":null},"content":[{"type":"text","text":"EXPIREAT","attrs":{}}]},{"type":"text","text":" 來設置過期時間的話也是有可能的。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"總而言之: 要知道大量key同時過期會對系統延遲造成影響。","attrs":{}}]},{"type":"horizontalrule","attrs":{}},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"Redis 看門狗","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"Redis2.6版本引進了redis看門狗(watchdog)軟件,這是個調試工具用於診斷Redis的延遲問題","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"這個看門狗軟件還是一個實驗性功能,當用於生產環境時,請小心並做好備份工作,可能有意想不到的問題影響正常的redis服務。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"當你沒有更好的工具追蹤問題時,可以使用它。","attrs":{}}]},{"type":"horizontalrule","attrs":{}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"這個功能是這樣工作的:","attrs":{}}]},{"type":"bulletedlist","content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"用戶通過命令CONFIG SET開啓軟件看門狗","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"Redis啓動監測程序監測自己的狀態","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"如果Redis檢測到服務器被某些操作阻塞了,並運行速度不夠快,也許是因爲延遲導致的,Redis就會在log文件中寫入一份關於被阻塞服務器的底層監測數據報表","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"用戶通過Redis Google Group發送消息給開發人員,消息包括看門狗報表。","attrs":{}}]}]}],"attrs":{}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"請注意,這項功能不能通過redis.conf文件開啓,因爲這項夠能設計之初就是面向正在運行的服務器,而且只是爲了調試程序。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"如果要開啓該功能,只需運行如下命令:","attrs":{}}]},{"type":"codeblock","attrs":{"lang":"shell"},"content":[{"type":"text","text":"CONFIG SET watchdog-period 500","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"時間間隔以毫秒爲單位。在上面的例子中,我指定了,當服務器檢測到500毫秒或更大的延遲的時候,才記錄延遲事件。最小的時間間隔是200毫秒。","attrs":{}}]},{"type":"horizontalrule","attrs":{}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"當你運行完了軟件看門狗,你可以通過設置時間間隔參數爲0來關閉看門狗。","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"需要注意的","attrs":{}},{"type":"text","text":":記得關閉看門狗,因爲開啓看門狗太長時間並不是一個好主意。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"以下的例子,你可以看到,當看門狗監測到延遲事件的時候,輸出日誌文件的內容:","attrs":{}}]},{"type":"codeblock","attrs":{"lang":"shell"},"content":[{"type":"text","text":"[8547 | signal handler] (1333114359)\n--- WATCHDOG TIMER EXPIRED ---\n/lib/libc.so.6(nanosleep+0x2d) [0x7f16b5c2d39d]\n/lib/libpthread.so.0(+0xf8f0) [0x7f16b5f158f0]\n/lib/libc.so.6(nanosleep+0x2d) [0x7f16b5c2d39d]\n/lib/libc.so.6(usleep+0x34) [0x7f16b5c62844]\n./redis-server(debugCommand+0x3e1) [0x43ab41]\n./redis-server(call+0x5d) [0x415a9d]\n./redis-server(processCommand+0x375) [0x415fc5]\n./redis-server(processInputBuffer+0x4f) [0x4203cf]\n./redis-server(readQueryFromClient+0xa0) [0x4204e0]\n./redis-server(aeProcessEvents+0x128) [0x411b48]\n./redis-server(aeMain+0x2b) [0x411dbb]\n./redis-server(main+0x2b6) [0x418556]\n/lib/libc.so.6(__libc_start_main+0xfd) [0x7f16b5ba1c4d]\n./redis-server() [0x411099]\n------","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"注意:例子中 ","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"DEBUG SLEEP 命令是用於阻塞服務器的。在不同的阻塞背景下,堆棧信息會有不同。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"如果收集到多個看門狗的監測堆棧信息,我們鼓勵你把這些信息發送到Redis Google Group:我們獲得越多的信息,我們就越容易分析得到你的服務器到底有什麼問題。","attrs":{}}]},{"type":"horizontalrule","attrs":{}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"附錄A:大內存頁的實驗","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"fork產生的延遲,可以通過大內存頁來減緩,只是需要耗費更大的內存。下面的附錄將詳細描述在Linux內核中實現的這個功能。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"雖然某些CPU會使用不同大小的頁面。AMD和Intel CPU可以支持2MB的頁面大小。這些頁面有個別名,叫做“大頁面”。某些操作系統可以實時地優化頁面大小,透明地把小頁面聚合成大頁面。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在Linux系統,顯式的huge page管理在2.6.16中得到支持,並且隱式透明的huge page管理也在2.6.38中得到支持。如果你的是最近的Linux發行版本(例如 RH6或者其派生版本),透明的huge page可以被開啓,並且你可以使用包含這項夠能的Redis版本。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"這個是在Linux中,實驗/使用huge page的最佳方法。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"現在,如果運行舊版本的Linux(RH5, SLES 10-11, 或者其派生版本),不要害怕使用一些技巧,Redis可以通過補丁來支持huge page。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"第一步,閱讀","attrs":{}},{"type":"link","attrs":{"href":"http://lwn.net/Articles/374424/","title":null,"type":null},"content":[{"type":"text","text":"Mel Gorman's primer on huge pages","attrs":{}}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"現在有兩個方法給Redis打補丁,讓它支持huge page","attrs":{}}]},{"type":"bulletedlist","content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"對於Redis 2.4,內置的jemalloc 分配器需要打上補丁。Pieter Noordhuis的補丁","attrs":{}},{"type":"link","attrs":{"href":"https://gist.github.com/1171054","title":null,"type":null},"content":[{"type":"text","text":"patch","attrs":{}}]},{"type":"text","text":" 。需要注意,這個補丁依賴於匿名mmap huge page的支持,這項功能只能從2.6.32之後纔得到支持,所以這個方法不能用於舊的版本(RH5 ,SLES 10, 和其派生版本)","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"對於Redis 2.2 或者2.4,附帶libc分配器,必須修改redis makefile,使Redis和","attrs":{}},{"type":"link","attrs":{"href":"http://libhugetlbfs.sourceforge.net/","title":null,"type":null},"content":[{"type":"text","text":"the libhugetlbfs library","attrs":{}}]},{"type":"text","text":"進行連接。這個是最直接的","attrs":{}},{"type":"link","attrs":{"href":"https://gist.github.com/1240452","title":null,"type":null},"content":[{"type":"text","text":"更改","attrs":{}}]}]}]}],"attrs":{}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"然後,系統必須配置爲支持huge page","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"以下命令分配和創建 N個huge page:","attrs":{}}]},{"type":"codeblock","attrs":{"lang":"shell"},"content":[{"type":"text","text":"$ sudo sysctl -w vm.nr_hugepages=","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"以下命令掛載huge page到文件系統","attrs":{}}]},{"type":"codeblock","attrs":{"lang":"shell"},"content":[{"type":"text","text":"$ sudo mount -t hugetlbfs none /mnt/hugetlbfs","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在所有的情況下,一旦Redis運行huge page(透明或者非透明),將會得到如下的好處:","attrs":{}}]},{"type":"bulletedlist","content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"由於fork引起的延遲將得到緩解。尤其是對超大的實例,尤其是在VM上運行的實例。","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"Redis速度得到提夠,是因爲CPU的轉換旁視緩衝(TLB)更有效的緩存頁面(例如命中率會更高)。不要期望有奇蹟發生,性能至多隻能提高一點。","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":" Redis內存不會再被換走,這樣就能避免由於虛擬內存造成的不必的延遲。很不幸,除了更多的複雜操作,還有redis使用huge page會帶來一個明顯的缺陷。COW機制的粒度是頁面。伴隨2MB頁面,頁面被後臺存儲操作修改的可能性是4KB頁面的512倍。實際的內存需要後臺存儲,所以可能性會增加很多,尤其是,當寫操作很隨機,並且伴隨很差的定位。通過huge page,使用兩倍的內存,而存儲將不再是理論的突發事件。它真的會發生。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"完整的性能評估結果可以參閱","attrs":{}},{"type":"link","attrs":{"href":"https://gist.github.com/1272254","title":null,"type":null},"content":[{"type":"text","text":"這裏","attrs":{}}]},{"type":"text","text":".","attrs":{}}]}]}],"attrs":{}},{"type":"horizontalrule","attrs":{}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}}]}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章