1.計算延遲時間:
使用–latency參數
如下參數表示平均超時時間0.03ms。node
redis-cli --latency -h 127.0.0.1 -p 6800 min: 0, max: 4, avg: 0.03 (12235 samples)
注意:因爲使用的是本機的迴環地址,因此這樣其實忽略了帶寬上的延遲
使用redis內部的延遲檢測子系統測試:見上一篇文章中「啓用延遲監控系統「部分。linux
2.延遲標準:
使用–intrinsic-latency參數
須要運行在redis server自己,用來檢測redis自己是否有延遲,在這種模式下,redis-cli不會鏈接redis server,它將衡量運行redis-cli進程自己最大的延遲時間。即redis的內部延遲ios
./redis-cli --intrinsic-latency 100 Max latency so far: 4 microseconds. Max latency so far: 7 microseconds. Max latency so far: 13 microseconds. Max latency so far: 28 microseconds. Max latency so far: 30 microseconds. Max latency so far: 39 microseconds. 32863264 total runs (avg 3 microseconds per run). Worst run took 13.00x times the avarege.
注意:後面的參數100表示100s
由測試結果能夠看出來,redis內部延遲僅爲39微秒(0.039毫秒),這會是一個比較好的消息,由於內部延遲不超過100微秒性能都是至關好的(生產環境中,數據量比較大的時候內部延遲超過100也是很正常的,不會對用戶的讀寫性能形成影響)
。web
不過須要注意的是,內部延遲嚴重依賴於cpu的load,若是你的系統有其餘應用在共享cpu,那麼對不起,你的內部延遲必定很大。
redis
不信?你能夠嘗試在一臺耗cpu的機器上跑redis並進行測試。算法
3.由網絡和通訊形成的延遲:
當用戶鏈接到Redis經過TCP/IP鏈接或Unix域鏈接,千兆網絡(1Gbit/s)的典型延遲大概200us
,而Unix域socket可能低到30us。這徹底基於你的網絡和系統硬件。在通訊自己之上,系統增長了更多的延遲(線程調度,CPU緩存,NUMA替換等等)。系統引發的延遲在虛擬機環境遠遠高於在物理機器環境。
緩存
1.實際狀況是即便Redis處理大多數命令在微秒之下,客戶機和服務器之間的交互也必然消耗系統相關的延遲。
2.一個高效的客戶機於是試圖經過捆綁多個命令在一塊兒的方式減小交互的次數。服務器和大多數客戶機支持這種方式。聚合命令象MSET/MGET也能夠用做這個目的。
服務器
在這裏會有幾個比較好的建議:網絡
*若是你負擔的起,儘量的使用物理機而不是虛擬機來作服務器 *不要常常的connect/disconnect與服務器的鏈接(尤爲是對基於web的應用),儘量的延長與服務器鏈接的時間。 *若是你的客戶端和服務器在同一臺主機上,則使用Unix域套接字 *儘可能使用聚合命令(MSET/MGET)或可變參數命令而不是pipelining *若是能夠儘可能使用pipelining而不是序列的往返命令。 *針對不適合使用原始pipelining的狀況,如某個命令的結果是後續命令的輸入,在之後的版本中redis提供了對服務器端的lua腳本的支持,實驗分支版本如今已經可使用了。
然而,大多數狀況下上述的優化方法是不須要的,除非你確實須要而且你對優化方法很熟悉的狀況下再使用上述方法。
4.redis的單線程天性:併發
redis使用單線程設計的,這就意味着單個進程須要使用一種多路複用的技術來服務全部的客戶端請求。意思就是在某一時刻,redis能夠服務於一個請求,而後其餘的請求會被頻繁的處理。這個和node.js工做模式很類似,然而,redis和node的處理機制都不會讓人感知到很慢。 這是由於,短期內redis就會完成單個請求,可是最重要的是這些產品被設計成系統調用時不能阻塞,好比一些在socket中讀數據或者寫數據操做。
5.慢命令形成的延遲:
單線程的一個結果是,當一個請求執行得很慢,其餘的客戶端調用就必須等待這個請求執行完畢。當執行GET、SET或者 LPUSH 命令的時候這不是個問題,由於這些操做可在很短的常數時間內完成。然而,對於多個元素的操做,像SORT, LREM, SUNION 這些,作兩個大數據集的交叉要花掉很長的時間。
官方文檔(http://redis.io/commands)提到了全部操做的算法複雜性。 在使用一個你不熟悉的命令以前系統的檢查它會是一個好辦法。
若是你對延遲有要求,那麼就不要執行涉及多個元素的慢操做,你可使用Redis的replication功能,把這類慢操做全都放到replica上執行。
此外,你能夠用你喜歡的進程監控程序(top, htop, prstat, 等…)來快速查看Redis進程的CPU使用率。若是traffic不高而CPU佔用很高,八成說明有慢操做。
重要提示:
通常來講,一個廣泛的延遲緣由都是使用了慢命令查詢,好比使用keys等命令(生產環境慎用),自從redis2.8以後,系統自帶一些命令能夠進行key的迭代,好比scan,sscan,hscan和zscan等
6.由fork產生的延遲:
Redis不管是爲了在後臺生成一個RDB文件,仍是爲了當AOF持久化方案被開啓時重寫Append Only文件,都會在後臺fork出一個進程這是redis惟一一個生成的子進程
。
fork操做(在主線程中被執行)自己會引起延遲。在大多數的類unix操做系統中,fork是一個很消耗的操做,由於它牽涉到複製不少與進程相關的對象。而這對於分頁表與虛擬內存機制關聯的系統尤其明顯。對於運行在一個linux/AMD64系統上的實例來講,內存會按照每頁4KB的大小分頁。爲了實現虛擬地址到物理地址的轉換,每個進程將會存儲一個分頁表(樹狀形式表現),分頁表將至少包含一個指向該進程地址空間的指針。因此一個空間大小爲24GB的redis實例,須要的分頁表大小爲 24GB/4KB*8 = 48MB。 當一個後臺的save命令執行時,實例會啓動新的線程去申請和拷貝48MB的內存空間。這將消耗一些時間和CPU資源,尤爲是在虛擬機上申請和初始化大塊內存空間時,消耗更加明顯
在不一樣系統中的Fork時間:
下面的列表比較了不一樣Redis實例的fork時間。數據包含正在執行的BGSAVE,並經過INFO指令查看thelatest_fork_usecfiled。 Linux beefy VM on VMware 6.0GB RSS forked 77 微秒 (每GB 12.8 微秒 ). Linux running on physical machine (Unknown HW) 6.1GB RSS forked 80 微秒(每GB 13.1微秒) Linux running on physical machine (Xeon @ 2.27Ghz) 6.9GB RSS forked into 62 微秒 (每GB 9 微秒). Linux VM on 6sync (KVM) 360 MB RSS forked in 8.2 微秒 (每GB 23.3 微秒). Linux VM on EC2 (Xen) 6.1GB RSS forked in 1460 微秒 (每GB 239.3 微秒). Linux VM on Linode (Xen) 0.9GBRSS forked into 382 微秒 (每GB 424 微秒).
7.透明大頁(transport huge pages)引發的延遲:
透明大頁,默認是always,啓用是爲了能夠管理更多的內存地址空間。可使用never關閉,以後會使用系統軟件的算法管理內存映射。
一般linux會將透明大頁開啓,在fork被調用後,redis產生的延遲被用來持久化到磁盤。內存大頁會引發一下問題:
1.fork被調用,共享內存大頁的兩個進程被建立
2.在一個系統比較活躍的實例裏,部分循環時間運行須要幾千個內存頁,期間引發的copy-on-write 會消耗幾乎全部的內存
3.這個將致使更大的延遲和使用更多的內存
所以,redis官方建議須要確保禁掉內存大頁:
echo never > /sys/kernel/mm/transparent_hugepage/enabled
8.swapping (操做系統分頁)引發的延遲:
Linux (以及其餘一些操做系統) 能夠把內存頁存儲在硬盤上,反之也能將存儲在硬盤上的內存頁再加載進內存,這種機制使得內存可以獲得更有效的利用。
若是內存頁被系統移到了swap文件裏,而這個內存頁中的數據剛好又被redis用到了(例如要訪問某個存儲在內存頁中的key),系統就會暫停redis進程直到把須要的頁數據從新加載進內存。這個操做由於牽涉到隨機I/O,因此很慢,會致使沒法預料的延遲。
系統老是要應對內存不足的壓力,由於每一個運行的進程都想申請更多的物理內存,而這些申請的內存的數量每每超過了實際擁有的內存。簡單來講就是redis使用的內存老是比可用的內存數量更多。 redis實例的數據,或者部分數據可能就不會被客戶端訪問,因此係統能夠把這部分閒置的數據置換到硬盤上。須要把全部數據都保存在內存中的狀況是很是罕見的。 一些進程會產生大量的讀寫I/O。由於文件一般都有緩存,這每每會致使文件緩存不斷增長,而後產生交換(swap)。請注意,redis RDB和AOF後臺線程都會產生大量文件。
所幸Linux提供了很好的工具來診斷這個問題,因此當延遲疑似是swap引發的,最簡單的辦法就是使用Linux提供的工具去確診。
首先查到redis進城的pid cd /proc/5454 在這裏你會發現一個名爲smaps 的文件,它描述了redis進程的內存佈局 (假定你使用的是Linux 2.6.16或者更新的版本)。這個文件包括了不少進程所使用內存的細節信息,其中有一項叫作Swap的正是咱們所關心的。不過僅看這一項是不夠的,由於smaps文件包括有redis進程的多個不一樣的的內存映射區域的使用狀況(進程的內存佈局遠不是線性排列那麼簡單)。 從咱們對全部進程的內存交換狀況感興趣以來,咱們首先要作的事情是使用grep命令顯示進程的smaps文件 $ cat smaps | grep 'Swap:' Swap: 0 kB Swap: 0 kB Swap: 0 kB Swap: 0 kB Swap: 0 kB Swap: 12 kB Swap: 4 kB Swap: 0 kB Swap: 0 kB Swap: 4 kB Swap: 4 kB 假如全部的數據顯示爲0kb或者某些數據偶爾顯示爲4kb,表示當前一切正常。實際上咱們的例子是一個真實的運行着Redis並每秒爲數百的用戶提供服務的網站,會顯示更多的交換頁。爲了研究是否存在一個嚴重的問題,咱們改變命令打印出分配的內存尺寸 $ cat smaps | egrep '^(Swap|Size)' Size: 316 kB Swap: 0 kB Size: 4 kB Swap: 0 kB Size: 8 kB Swap: 0 kB Size: 40 kB Swap: 0 kB Size: 132 kB Swap: 0 kB Size: 720896 kB Swap: 12 kB Size: 4096 kB Swap: 156 kB 在輸出信息中,你能看到有一個720896kb的內存分配(有12kb的交換)還有一個156kb的交換是另外一個進程的。基本上咱們的內存只會有很小的內存交換,所以不會產生任何的問題 假如進程的內存有至關部分花在了swap上,那麼你的延遲可能就與swap有關。假如redis出現這種狀況那麼能夠用 vmstat 命令來驗證一下猜想: $ vmstat 1 procs -----------memory---------- ---swap-- -----io---- -system-- ----cpu---- r b swpd free buff cache si so bi bo in cs us sy id wa 0 0 3980 697932 147180 1406456 0 0 2 2 2 0 4 4 91 0 0 0 3980 697428 147180 1406580 0 0 0 0 19088 16104 9 6 84 0 0 0 3980 697296 147180 1406616 0 0 0 28 18936 16193 7 6 87 0 0 0 3980 697048 147180 1406640 0 0 0 0 18613 15987 6 6 88 0 2 0 3980 696924 147180 1406656 0 0 0 0 18744 16299 6 5 88 0 0 0 3980 697048 147180 1406688 0 0 0 4 18520 15974 6 6 88 0 輸出中咱們最感興趣的兩行是si 和 so,這兩行分別統計了從swap文件恢復到內存的數量和swap到文件的內存數量。若是在這兩行發現了非0值那麼就說明系統正在進行swap。 最後,能夠用iostat命令來查看系統的全局I/O行爲。 $ iostat -xk 1 avg-cpu: %user %nice %system %iowait %steal %idle 13.55 0.04 2.92 0.53 0.00 82.95 Device: rrqm/s wrqm/s r/s w/s rkB/s wkB/s avgrq-sz avgqu-sz await svctm %util sda 0.77 0.00 0.01 0.00 0.40 0.00 73.65 0.00 3.62 2.58 0.00 sdb 1.27 4.75 0.82 3.54 38.00 32.32 32.19 0.11 24.80 4.24 1.85 若是確認延遲是因爲swap引發的,那麼就須要減少系統的內存壓力,要麼給機器增長內存,要麼不要在同一個機器上運行其餘消耗內存的程序。
9.AOF和磁盤I/O形成的延遲:
另外一個延遲的根源是Redis的AOF(僅附加文件)模式。AOF基本上是經過兩個系統間的調用來完成工做的。 一個是寫,用來寫數據到AOF, 另一個是文件數據同步,經過清除硬盤上空核心文件的緩衝來保證用戶指定的持久級別。
包括寫和文件數據同步的調用均可以致使延遲的根源。 寫實例能夠阻塞系統範圍的同步操做,也能夠阻塞當輸出的緩衝區滿而且內核須要清空到硬盤來接受新的寫入的操做。
文件數據同步對於延遲的影響很是大,由於它涉及到好幾步調用,可能要花掉幾毫秒以至幾秒的時間,特別是在還有其餘進程後也在佔用I/O的狀況下。
咱們來看看當使用AOF的時候如何配置來下降延遲:
經過設置AOF相關的appendfsync項,可使用三種不一樣的方式來執行文件同步(也能夠在運行時使用config set 命令來修改這個配置)
**appendfsync 的值設置爲no,redis不執行fsync。這種狀況下形成延遲的惟一緣由就是寫操做。這種延遲沒有辦法能夠解決,由於redis接收到數據的速度是不可控的,不過這種狀況也不常見,除非有其餘的進程佔用I/O使得硬盤速度忽然降低。 **appendfsync 的值設置爲everysec,每秒都會執行fsync。fsync 由一個單獨線程執行,若是須要寫操做的時候有fsync正在執行redis就會用一個buffer來延遲寫入2秒(由於在Linux若是一個fsync 正在運行那麼對該文件的寫操做就會被堵塞)。若是fsync 耗時過長(譯者注:超過了2秒),即便fsync 還在進行redis也會執行寫操做,這就會形成延遲。 **appendfsync 的值設置爲always ,fsync 會在每次寫操做返回成功代碼以前執行(事實上redis會積累多個命令在一次fsync 過程當中執行)。這種模式下的性能表現是很是差勁的,因此最好使用一個快速的磁盤和文件系統以加快fsync 的執行。
大多數redis用戶都會把這個值設成 no 或者 everysec。要減小延遲,最好避免在同一個機器上有其餘耗費I/O的程序。
經驗中發現其實能夠把主的持久化設置rdb,從設置成aof,或者aof直接關閉。由於aof會將因此的寫操做進行記錄。固然用SSD也有益於下降延遲,不過即便不使用SSD,若是能有冗餘的硬盤專用於AOF也會減小尋址時間,從而下降延遲。
若是你想診斷AOF相關的延遲緣由可使用strace 命令:
strace -p $(pidof redis-server) -T -e trace=fdatasync,write
上面的命令會展現redis主線程裏全部的fdatasync系統調用。不包括後臺線程執行的fdatasync 調用。不過由於write也會向客戶端寫數據,因此用上面的命令極可能會得到許多與磁盤I/O沒有關係的結果。彷佛沒有辦法讓strace 只顯示慢系統調用,因此要用下面的命令:
strace -f -p $(pidof redis-server) -T -e trace=fdatasync,write 2>&1 | grep -v '0.0' | grep -v unfinished
10.數據過時形成的延遲:
redis有兩種方式來驅逐過時的key: lazy方式,在key被請求的時候才檢查是否過時。 active方式,每0.1秒進行一次過時檢查。
active過時模式是自適應的,每過100毫秒開始一次過時檢查(每秒10次),每次做以下操做:
- **根據 REDIS_EXPIRELOOKUPS_PER_CRON 的值刪除已通過期的key(是指若是過時的key數量超過了REDIS_EXPIRELOOKUPS_PER_CRON 的值纔會啓動過時操做,太少就沒必要了。這個值默認爲10)
- **假如超過25%(是指REDIS_EXPIRELOOKUPS_PER_CRON這個值的25%)的key已通過期,則重複一遍檢查失效的過程。
11.經常使用的檢查命令:
1.查看前num條慢命令,這個所謂的慢是能夠經過參數slowlog-log-slower-than設定的,默認10000us,也就是10ms >slowlog get num 2.查看當前實例中哪些key比較佔空間(redis-cli的參數) #redis-cli --bigkeys 3.查看redis相關的監控信息 >info 相關命令(memory cpu replication stats clients) 注意:客戶端鏈接數默認限制爲10000,生產測試發現超過5000就會影響;total_commands_processed、instantaneous_ops_per_sec、net_io_in_per_sec、net_io_out_per_sec、total_commands、connected_clients、used_memory_human、used_memory_peak_human這些指標都須要關注下; 4.測試qps >redis-benchmark -h 127.0.0.1 -p 6379 -c 50 -n 10000 -d 2 50個併發連接,10000個請求,每一個請求2kb。
寫在最後:
維護生產環境中,更多須要排查的其實就是超時問題,因爲形成超時緣由比較多,所以會給運維同事形成不少困擾,但現實狀況每每不是那樣子的,由於做爲一個基礎服務,在上線以前就須要對一些基本環境進行優化,好比說系統層面cpu以及內存的調優,並且生產環境通常也不會用虛機去跑比較重要並且吞吐比較高的redis吧,除非是真窮了,這樣說來超時的緣由其實就很小了。反正在個人維護過程當中,大多數狀況是用戶使用一些非業務操做命令,什麼意思呢,就是keys啦之類的致使redis堵塞,還有一種狀況就是用戶對redis的命令使用不是特別熟悉,由於原始命令裏支持不少聚合命令,好比mset,mget,mhget等等,還有管道的一些使用。另外還遇到過一次超時基本上時由於客戶端鏈接數太高,當時已經到8k+,臨時採起措施後,客戶端鏈接數降下來其實就沒有什麼事了。 那麼問題來了,爲何會這樣呢?運維和用戶之間的溝通太少,彼此之間你不懂我我不懂你,因此形成的redis自己的誤用、濫用。等真正出現問題的時候各自都有理,運維說:你丫生產不要用keys好吧,單線程的東西,遍歷一遍key業務還要不要訪問數據啊;用戶說:麻痹,我只管開發業務系統了,redis推薦的使用方法和規則你又沒告訴我。 其實,都沒有錯,錯在運維和用戶之間那堵牆沒有鏈接起來。