1. Redis為什麼是單線程的
MongoDB不多說,不是一個類型的東西,Redis相對Memcached來說功能和特性上的優勢已經很明顯了。而對於性能,Redis作者的說法是平均到單個核上的性能,在單條數據不大的情況下Redis更好。為什麼這么說呢,理由就是Redis是單線程運行的。
2. redis是個單線程的程序,為什麼會這么快呢
純內存資料庫,如果只是簡單的 key-value,內存不是瓶頸。一般情況下,hash 查找可以達到每秒數百萬次的數量級。
瓶頸在於網路 IO 上。
根據你測的的 10000/s 來看,客戶端和 redis 應該是部署在兩台不同的機器,並且是使用同步的方式請求 redis. 每次請求需要通過網路把請求發送到 redis 所在的機器,然後等待 redis 返回數據。時間大部分消耗在網路傳輸中。
如果把 redis 和客戶端放在同一台機器,網路延遲會更小,一般情況下可以打到 60000 次每秒甚至更高,取決於機器性能。
鎖不是影響性能的主要因素。線程鎖 (mutex_lock) 只有在遇到沖突的情況下性能會下降,而正常情況下,遇到沖突的概率很低。如果只是簡單的加鎖、釋放鎖速度是非常快的,每秒鍾上千萬次沒問題。memcache 內部用到了大量的鎖,並沒有見到性能降低。
線程也不是影響吞吐量的重要因素。如第一點來說,一般情況下,程序處理內存數據的速度遠高於網卡接收的速度。使用線程好處是可以同時處理多條連接,在極端情況下,可能會提高響應速度。
使用 epoll 或 libevent 等因為非同步非阻塞 IO 編程只能這么做。與之對應的是同步阻塞 IO 編程,使用多進程或多線程實現多條連接的處理,比如 apache。一般情況下,非同步非阻塞 IO 模型性能是遠高於同步阻塞 IO 模型的,可以參考 nginx 與 apache 性能的對比。
libevent 並不比 redis 自己實現的 ae_event 慢,代碼多是應為 ae_event 只實現了 redis 需要的功能,而 libevent 則具有更多的功能,比如更快的定時器、buffer event 模型,甚至自帶了 DNS、HTTP 協議的處理。並且 libevent 更通用,而 redis 只專注於 linux 平台。
最後回答問題,快在哪? 1、純內存操作 2、非同步非阻塞 IO
3. redis如何實現多線程
redis是以單進程的形式運行的,命令是一個接著一個執行的,能很好的解決程序的並發問題
所以在當數據涉及並發問題
比如秒殺
我們就是把數據線存進redis
然後用戶請求的時候在redis中減庫存redis是單線程所以不會減超
redis減成功了之後就拒絕之後的請求然後在資料庫減庫存
這樣就不會出現庫存為負的問題
這就是基本的運作原理
4. Redis到底是多線程還是單線程
單線程指的是網路請求模塊使用了一個線程,其他模塊用了多個線程。
可能因為用了非同步IO模型,單線程的效率已經足夠高了。
5. 單線程的redis如何利用多核cpu機器
可以在同一個伺服器部署多個Redis的實例,並把他們當作不同的伺服器來使用,在某些時候,無論如何一個伺服器是不夠的, 所以,如果你想使用多個CPU,你可以考慮一下分片(shard)
redis的讀取和處理性能非常強大,一般伺服器的cpu都不會是性能瓶頸。redis的性能瓶頸主要集中在內存和網路方面。所以,如果使用的redis命令多為O(N)、O(log(N))時間復雜度,那麼基本上不會出現cpu瓶頸的情況。
但是如果你確實需要充分使用多核cpu的能力,那麼需要在單台伺服器上運行多個redis實例(主從部署/集群化部署),並將每個redis實例和cpu內核進行綁定(使用 taskset命令)。
如果需要進行集群化部署,你需要對redis進行分片存儲。
6. redis為什麼是單線程
Redis採用的是基於內存的採用的是單進程單線程模型的KV資料庫,由C語言編寫。官方提供的數據是可以達到100000+的qps。這個數據不比採用單進程多線程的同樣基於內存的KV資料庫Memcached差。
Redis並沒有直接使用Libevent,而是自己完成了一個非常輕量級的對select、epoll、evport、kqueue這些通用的介面的實現。在不同的系統調用選用適合的介面,linux下默認是epoll。
因為Libevent比較重更通用代碼量也就很龐大,擁有很多Redis用不上的功能,Redis為了追求「輕巧」並且去除依賴,就選擇自己去封裝了一套。
單線程優點:
同步應用程序的開發比較容易,但由於需要在上一個任務完成後才能開始新的任務,所以其效率通常比多線程應用程序低。如果完成同步任務所用的時間比預計時間長,應用程序可能會不響應。多線程處理可以同時運行多個過程。例如,文字處理器應用程序在您處理文檔的同時,可以檢查拼寫(作為單獨的任務)。
7. 既然redis是單線程模型,怎麼會出現並發訪問操作同一份數據
1,一個redis命令可能是復合命令吧
2,多線程客戶端也可能同時讀寫一個KEY吧。雖然redis是單線程,本身不會出什麼問題。但對於客戶端業務邏輯來說就顯得很詭異了。
8. Redis持久化
Redis支持RDB和AOF兩種持久化機制,持久化功能有效地避免因進程退出造成的數據丟失問題,當下次重啟時利用之前持久化的文件即可實現數據恢復。理解掌握持久化機制對於Redis運維非常重要。本章內容如下:
·首先介紹RDB、AOF的配置和運行流程,以及控制持久化的相關命令,如bgsave和bgrewriteaof。
·其次對常見持久化問題進行分析定位和優化。
·最後結合Redis常見 的單機多實例部署場景進行優化。
5.1RDB
RDB持久化是把當前進程數據生成快照保存到硬碟的過程,觸發RDB持久化過程分為手動觸發和自動觸發。
5.1.1觸發機制
手動觸發分別對應save和bgsave命令:
·save命令:阻塞當前Redis伺服器,直到RDB過程完成為止,對於內存比較大的實例會造成長時間阻塞,線上環境不建議使用。運行save命令對應
的Redis日誌如下:
* DB saved on disk
·bgsave命令:Redis進程執行fork操作創建子進程,RDB持久化過程由子進程負責,完成後自動結束。阻塞只發生在fork階段,一般時間很短。運行bgsave命令對應的Redis日誌如下:
* Background saving started by pid 3151
* DB saved on disk
* RDB: 0 MB of memory used by -on-write
* Background saving terminated with success
顯然bgsave命令是針對save阻塞問題做的優化。因此Redis內部所有的涉及RDB的操作都採用bgsave的方式,而save命令已經廢棄。
除了執行命令手動觸發之外,Redis內部還存在自動觸發RDB的持久化機制,例如以下場景:
1)使用save相關配置,如「save m n」。表示m秒內數據集存在n次修改時,自動觸發bgsave。
2)如果從節點執行全量復制操作,主節點自動執行bgsave生成RDB文件並發送給從節點,更多細節見6.3節介紹的復制原理。
3)執行debug reload命令重新載入Redis時,也會自動觸發save操作。
4)默認情況下執行shutdown命令時,如果沒有開啟AOF持久化功能則自動執行bgsave。
5.1.2流程說明
bgsave是主流的觸發RDB持久化方式,下面根據圖5-1了解它的運作流程。
1)執行bgsave命令,Redis父進程判斷當前是否存在正在執行的子進程,如RDB/AOF子進程,如果存在bgsave命令直接返回。
2)父進程執行fork操作創建子進程,fork操作過程中父進程會阻塞,通過info stats命令查看latest_fork_usec選項,可以獲取最近一個fork操作的耗時,單位為微秒。
3)父進程fork完成後,bgsave命令返回「Background saving started」信息並不再阻塞父進程,可以繼續響應其他命令。
4)子進程創建RDB文件,根據父進程內存生成臨時快照文件,完成後對原有文件進行原子替換。執行lastsave命令可以獲取最後一次生成RDB的時間,對應info統計的rdb_last_save_time選項。
5)進程發送信號給父進程表示完成,父進程更新統計信息,具體見info Persistence下的rdb_*相關選項。
5.1.3RDB文件的處理
保存:RDB文件保存在dir配置指定的目錄下,文件名通過dbfilename配置指定。可以通過執行config set dir{newDir}和config setdbfilename{newFileName}運行期動態執行,當下次運行時RDB文件會保存到新目錄。
運維提示
當遇到壞盤或磁碟寫滿等情況時,可以通過config set dir{newDir}在線修改文件路徑到可用的磁碟路徑,之後執行bgsave進行磁碟切換,同樣適用於AOF持久化文件。
壓縮:Redis默認採用LZF演算法對生成的RDB文件做壓縮處理,壓縮後的文件遠遠小於內存大小,默認開啟,可以通過參數config set rdbcompression{yes|no}動態修改。
運維提示
雖然壓縮RDB會消耗CPU,但可大幅降低文件的體積,方便保存到硬碟或通過網路發送給從節點,因此線上建議開啟。
校驗:如果Redis載入損壞的RDB文件時拒絕啟動,並列印如下日誌:
# Short read or OOM loading DB. Unrecoverable error, aborting now.
這時可以使用Redis提供的redis-check-mp工具檢測RDB文件並獲取對應的錯誤報告。
5.1.4RDB的優缺點
RDB的優點:
·RDB是一個緊湊壓縮的二進制文件,代表Redis在某個時間點上的數據快照。非常適用於備份,全量復制等場景。比如每6小時執行bgsave備份,並把RDB文件拷貝到遠程機器或者文件系統中(如hdfs),用於災難恢復。
·Redis載入RDB恢復數據遠遠快於AOF的方式。
RDB的缺點:
·RDB方式數據沒辦法做到實時持久化/秒級持久化。因為bgsave每次運行都要執行fork操作創建子進程,屬於重量級操作,頻繁執行成本過高。
·RDB文件使用特定二進制格式保存,Redis版本演進過程中有多個格式的RDB版本,存在老版本Redis服務無法兼容新版RDB格式的問題。針對RDB不適合實時持久化的問題,Redis提供了AOF持久化方式來解決。
5.2AOF
AOF(append only file)持久化:以獨立日誌的方式記錄每次寫命令,重啟時再重新執行AOF文件中的命令達到恢復數據的目的。AOF的主要作用是解決了數據持久化的實時性,目前已經是Redis持久化的主流方式。理解掌握好AOF持久化機制對我們兼顧數據安全性和性能非常有幫助。
5.2.1使用AOF
開啟AOF功能需要設置配置:appendonly yes,默認不開啟。AOF文件名通過appendfilename配置設置,默認文件名是appendonly.aof。保存路徑同RDB持久化方式一致,通過dir配置指定。AOF的工作流程操作:命令寫入(append)、文件同步(sync)、文件重寫(rewrite)、重啟載入(load),如圖5-2所示。
1)所有的寫入命令會追加到aof_buf(緩沖區)中。
2)AOF緩沖區根據對應的策略向硬碟做同步操作。
3)隨著AOF文件越來越大,需要定期對AOF文件進行重寫,達到壓縮的目的。
4)當Redis伺服器重啟時,可以載入AOF文件進行數據恢復。了解AOF工作流程之後,下面針對每個步驟做詳細介紹。
5.2.2命令寫入
AOF命令寫入的內容直接是文本協議格式。例如set hello world這條命令,在AOF緩沖區會追加如下文本:*3\r\n$3\r\nset\r\n$5\r\nhello\r\n$5\r\nworld\r\n
Redis協議格式具體說明見4.1客戶端協議小節,這里不再贅述,下面介
紹關於AOF的兩個疑惑:
1)AOF為什麼直接採用文本協議格式?可能的理由如下:
·文本協議具有很好的兼容性。
·開啟AOF後,所有寫入命令都包含追加操作,直接採用協議格式,避免了二次處理開銷。
·文本協議具有可讀性,方便直接修改和處理。
2)AOF為什麼把命令追加到aof_buf中?Redis使用單線程響應命令,如果每次寫AOF文件命令都直接追加到硬碟,那麼性能完全取決於當前硬碟負載。先寫入緩沖區aof_buf中,還有另一個好處Redis可以提供多種緩沖區同步硬碟的策略,在性能和安全性方面做出平衡。
5.2.3文件同步
Redis提供了多種AOF緩沖區同步文件策略,由參數appendfsync控制,不同值的含義如表5-1所示。
表5-1AOF緩沖區同步文件策略
系統調用write和fsync說明:
·write操作會觸發延遲寫(delayed write)機制。Linux在內核提供頁緩沖區用來提高硬碟IO性能。write操作在寫入系統緩沖區後直接返回。同步硬碟操作依賴於系統調度機制,例如:緩沖區頁空間寫滿或達到特定時間周期。同步文件之前,如果此時系統故障宕機,緩沖區內數據將丟失。
·fsync針對單個文件操作(比如AOF文件),做強制硬碟同步,fsync將阻塞直到寫入硬碟完成後返回,保證了數據持久化。除了write、fsync,Linux還提供了sync、fdatasync操作,具體API說明參
見:http://linux.die.net/man/2/write,http://linux.die.net/man/2/fsync,http://linux.die.net/man/2/sync
·配置為always時,每次寫入都要同步AOF文件,在一般的SATA硬碟上,Redis只能支持大約幾百TPS寫入,顯然跟Redis高性能特性背道而馳,不建議配置。
·配置為no,由於操作系統每次同步AOF文件的周期不可控,而且會加大每次同步硬碟的數據量,雖然提升了性能,但數據安全性無法保證。
·配置為everysec,是建議的同步策略,也是默認配置,做到兼顧性能和數據安全性。理論上只有在系統突然宕機的情況下丟失1秒的數據。(嚴格來說最多丟失1秒數據是不準確的,5.3節會做具體介紹到。)
5.2.4重寫機制
隨著命令不斷寫入AOF,文件會越來越大,為了解決這個問題,Redis引入AOF重寫機制壓縮文件體積。AOF文件重寫是把Redis進程內的數據轉化為寫命令同步到新AOF文件的過程。
重寫後的AOF文件為什麼可以變小?有如下原因:
1)進程內已經超時的數據不再寫入文件。
2)舊的AOF文件含有無效命令,如del key1、hdel key2、srem keys、set
a111、set a222等。重寫使用進程內數據直接生成,這樣新的AOF文件只保留最終數據的寫入命令。
3)多條寫命令可以合並為一個,如:lpush list a、lpush list b、lpush list c可以轉化為:lpush list a b c。為了防止單條命令過大造成客戶端緩沖區溢出,對於list、set、hash、zset等類型操作,以64個元素為界拆分為多條。
AOF重寫降低了文件佔用空間,除此之外,另一個目的是:更小的AOF文件可以更快地被Redis載入。AOF重寫過程可以手動觸發和自動觸發:
·手動觸發:直接調用bgrewriteaof命令。
·自動觸發:根據auto-aof-rewrite-min-size和auto-aof-rewrite-percentage參數確定自動觸發時機。
·auto-aof-rewrite-min-size:表示運行AOF重寫時文件最小體積,默認為64MB。
·auto-aof-rewrite-percentage:代表當前AOF文件空間(aof_current_size)和上一次重寫後AOF文件空間(aof_base_size)的比值。自動觸發時機=aof_current_size>auto-aof-rewrite-min-size&&(aof_current_size-aof_base_size)/aof_base_size>=auto-aof-rewrite-percentage其中aof_current_size和aof_base_size可以在info Persistence統計信息中查看。當觸發AOF重寫時,內部做了哪些事呢?下面結合圖5-3介紹它的運行流程。
圖5-3AOF重寫運作流程
流程說明:
1)執行AOF重寫請求。
如果當前進程正在執行AOF重寫,請求不執行並返回如下響應:
ERR Background append only file rewriting already in progress
如果當前進程正在執行bgsave操作,重寫命令延遲到bgsave完成之後再執行,返回如下響應:
Background append only file rewriting scheled
2)父進程執行fork創建子進程,開銷等同於bgsave過程。
3.1)主進程fork操作完成後,繼續響應其他命令。所有修改命令依然寫入AOF緩沖區並根據appendfsync策略同步到硬碟,保證原有AOF機制正確性。
3.2)由於fork操作運用寫時復制技術,子進程只能共享fork操作時的內存數據。由於父進程依然響應命令,Redis使用「AOF重寫緩沖區」保存這部分新數據,防止新AOF文件生成期間丟失這部分數據。
4)子進程根據內存快照,按照命令合並規則寫入到新的AOF文件。每次批量寫入硬碟數據量由配置aof-rewrite-incremental-fsync控制,默認為32MB,防止單次刷盤數據過多造成硬碟阻塞。
5.1)新AOF文件寫入完成後,子進程發送信號給父進程,父進程更新統計信息,具體見info persistence下的aof_*相關統計。
5.2)父進程把AOF重寫緩沖區的數據寫入到新的AOF文件。
5.3)使用新AOF文件替換老文件,完成AOF重寫。
5.2.5重啟載入
AOF和RDB文件都可以用於伺服器重啟時的數據恢復。如圖5-4所示,表示Redis持久化文件載入流程。
流程說明:
1)AOF持久化開啟且存在AOF文件時,優先載入AOF文件,列印如下日誌:
* DB loaded from append only file: 5.841 seconds
2)AOF關閉或者AOF文件不存在時,載入RDB文件,列印如下日誌:
* DB loaded from disk: 5.586 seconds
3)載入AOF/RDB文件成功後,Redis啟動成功。
4)AOF/RDB文件存在錯誤時,Redis啟動失敗並列印錯誤信息。
5.2.6文件校驗
載入損壞的AOF文件時會拒絕啟動,並列印如下日誌:
# Bad file format reading the append only file: make a backup of your AOF file,
then use ./redis-check-aof --fix <filename>
運維提示
對於錯誤格式的AOF文件,先進行備份,然後採用redis-check-aof--fix命令進行修復,修復後使用diff-u對比數據的差異,找出丟失的數據,有些可以人工修改補全。
AOF文件可能存在結尾不完整的情況,比如機器突然掉電導致AOF尾部文件命令寫入不全。Redis為我們提供了aof-load-truncated配置來兼容這種情況,默認開啟。載入AOF時,當遇到此問題時會忽略並繼續啟動,同時列印
如下警告日誌:
# !!! Warning: short read while loading the AOF file !!!
# !!! Truncating the AOF at offset 397856725 !!!
# AOF loaded anyway because aof-load-truncated is enabled
5.3問題定位與優化
Redis持久化功能一直是影響Redis性能的高發地,本節我們結合常見的持久化問題進行分析定位和優化。
5.3.1fork操作
當Redis做RDB或AOF重寫時,一個必不可少的操作就是執行fork操作創建子進程,對於大多數操作系統來說fork是個重量級錯誤。雖然fork創建的子進程不需要拷貝父進程的物理內存空間,但是會復制父進程的空間內存頁表。例如對於10GB的Redis進程,需要復制大約20MB的內存頁表,因此fork操作耗時跟進程總內存量息息相關,如果使用虛擬化技術,特別是Xen虛擬機,fork操作會更耗時。
fork耗時問題定位:對於高流量的Redis實例OPS可達5萬以上,如果fork操作耗時在秒級別將拖Redis幾萬條命令執行,對線上應用延遲影響非常明顯。正常情況下fork耗時應該是每GB消耗20毫秒左右。可以在info stats統計中查latest_fork_usec指標獲取最近一次fork操作耗時,單位微秒。
如何改善fork操作的耗時:
1)優先使用物理機或者高效支持fork操作的虛擬化技術,避免使用Xen。
2)控制Redis實例最大可用內存,fork耗時跟內存量成正比,線上建議每個Redis實例內存控制在10GB以內。
3)合理配置Linux內存分配策略,避免物理內存不足導致fork失敗,具體細節見12.1節「Linux配置優化」。
4)降低fork操作的頻率,如適度放寬AOF自動觸發時機,避免不必要的全量復制等。
5.3.2子進程開銷監控和優化
子進程負責AOF或者RDB文件的重寫,它的運行過程主要涉及CPU、內存、硬碟三部分的消耗。
1.CPU
·CPU開銷分析。子進程負責把進程內的數據分批寫入文件,這個過程屬於CPU密集操作,通常子進程對單核CPU利用率接近90%.
·CPU消耗優化。Redis是CPU密集型服務,不要做綁定單核CPU操作。由於子進程非常消耗CPU,會和父進程產生單核資源競爭。不要和其他CPU密集型服務部署在一起,造成CPU過度競爭。如果部署多個Redis實例,盡量保證同一時刻只有一個子進程執行重寫工作,具體細節見5.4節多實例部署」。
2.內存
·內存消耗分析。子進程通過fork操作產生,佔用內存大小等同於父進程,理論上需要兩倍的內存來完成持久化操作,但Linux有寫時復制機制(-on-write)。父子進程會共享相同的物理內存頁,當父進程處理寫請求時會把要修改的頁創建副本,而子進程在fork操作過程中共享整個父進程內存快照。
·內存消耗監控。RDB重寫時,Redis日誌輸出容如下:
* Background saving started by pid 7692
* DB saved on disk
* RDB: 5 MB of memory used by -on-write
* Background saving terminated with success
如果重寫過程中存在內存修改操作,父進程負責創建所修改內存頁的副本,從日誌中可以看出這部分內存消耗了5MB,可以等價認為RDB重寫消耗了5MB的內存。
AOF重寫時,Redis日誌輸出容如下:
* Background append only file rewriting started by pid 8937
* AOF rewrite child asks to stop sending diffs.
* Parent agreed to stop sending diffs. Finalizing AOF...
* Concatenating 0.00 MB of AOF diff received from parent.
* SYNC append only file rewrite performed
* AOF rewrite: 53 MB of memory used by -on-write
* Background AOF rewrite terminated with success
* Resial parent diff successfully flushed to the rewritten AOF (1.49 MB)
* Background AOF rewrite finished successfully
父進程維護頁副本消耗同RDB重寫過程類似,不同之處在於AOF重寫需要AOF重寫緩沖區,因此根據以上日誌可以預估內存消耗為:53MB+1.49MB,也就是AOF重寫時子進程消耗的內存量。
運維提示
編寫shell腳本根據Redis日誌可快速定位子進程重寫期間內存過度消耗情況。
內存消耗優化:
1)同CPU優化一樣,如果部署多個Redis實例,盡量保證同一時刻只有一個子進程在工作。
2)避免在大量寫入時做子進程重寫操作,這樣將導致父進程維護大量頁副本,造成內存消耗。Linux kernel在2.6.38內核增加了Transparent Huge Pages(THP),支持huge page(2MB)的頁分配,默認開啟。當開啟時可以降低fork創建子進程的速度,但執行fork之後,如果開啟THP,復制頁單位從原來4KB變為2MB,會大幅增加重寫期間父進程內存消耗。建議設置「sudo echo never>/sys/kernel/mm/transparent_hugepage/enabled」關閉THP。更多THP細節和配置見12.1Linux配置優化」。
3.硬碟
·硬碟開銷分析。子進程主要職責是把AOF或者RDB文件寫入硬碟持久化。勢必造成硬碟寫入壓力。根據Redis重寫AOF/RDB的數據量,結合系統工具如sar、iostat、iotop等,可分析出重寫期間硬碟負載情況。·硬碟開銷優化。優化方法如下:
a)不要和其他高硬碟負載的服務部署在一起。如:存儲服務、消息隊列服務等。
b)AOF重寫時會消耗大量硬碟IO,可以開啟配置no-appendfsync-on-rewrite,默認關閉。表示在AOF重寫期間不做fsync操作。
c)當開啟AOF功能的Redis用於高流量寫入場景時,如果使用普通機械磁碟,寫入吞吐一般在100MB/s左右,這時Redis實例的瓶頸主要在AOF同步硬碟上。
d)對於單機配置多個Redis實例的情況,可以配置不同實例分盤存儲AOF文件,分攤硬碟寫入壓力。運維提示
配置no-appendfsync-on-rewrite=yes時,在極端情況下可能丟失整個AOF重寫期間的數據,需要根據數據安全性決定是否配置。
5.3.3AOF追加阻塞
當開啟AOF持久化時,常用的同步硬碟的策略是everysec,用於平衡性能和數據安全性。對於這種方式,Redis使用另一條線程每秒執行fsync同步硬碟。當系統硬碟資源繁忙時,會造成Redis主線程阻塞,如圖5-5所示。
阻塞流程分析:
1)主線程負責寫入AOF緩沖區。
2)AOF線程負責每秒執行一次同步磁碟操作,並記錄最近一次同步時間。
3)主線程負責對比上次AOF同步時間:
·如果距上次同步成功時間在2秒內,主線程直接返回。
·如果距上次同步成功時間超過2秒,主線程將會阻塞,直到同步操作完成。
通過對AOF阻塞流程可以發現兩個問題:
1)everysec配置最多可能丟失2秒數據,不是1秒。
2)如果系統fsync緩慢,將會導致Redis主線程阻塞影響效率。
AOF阻塞問題定位:
1)發生AOF阻塞時,Redis輸出如下日誌,用於記錄AOF fsync阻塞導致拖慢Redis服務的行為:
Asynchronous AOF fsync is taking too long (disk is busy). Writing the AOF buffer
without waiting for fsync to complete, this may slow down Redis
2)每當發生AOF追加阻塞事件發生時,在info Persistence統計中,aof_delayed_fsync指標會累加,查看這個指標方便定位AOF阻塞問題。
3)AOF同步最多允許2秒的延遲,當延遲發生時說明硬碟存在高負載問題,可以通過監控工具如iotop,定位消耗硬碟IO資源的進程。優化AOF追加阻塞問題主要是優化系統硬碟負載,優化方式見上一節。
5.4多實例部署
Redis單線程架構導致無法充分利用CPU多核特性,通常的做法是在一台機器上部署多個Redis實例。當多個實例開啟AOF重寫後,彼此之間會產生對CPU和IO的競爭。本節主要介紹針對這種場景的分析和優化。上一節介紹了持久化相關的子進程開銷。對於單機多Redis部署,如果同一時刻運行多個子進程,對當前系統影響將非常明顯,因此需要採用一種措施,把子進程工作進行隔離。Redis在info Persistence中為我們提供了監控子進程運行狀況的度量指標,如表5-2所示。
我們基於以上指標,可以通過外部程序輪詢控制AOF重寫操作的執行,整個過程如圖5-6所示。
流程說明:
1)外部程序定時輪詢監控機器(machine)上所有Redis實例。
2)對於開啟AOF的實例,查看(aof_current_size-aof_base_size)/aof_base_size確認增長率。
3)當增長率超過特定閾值(如100%),執行bgrewriteaof命令手動觸發當前實例的AOF重寫。
4)運行期間循環檢查aof_rewrite_in_progress和aof_current_rewrite_time_sec指標,直到AOF重寫結束。
5)確認實例AOF重寫完成後,再檢查其他實例並重復2)~4)步操作。從而保證機器內每個Redis實例AOF重寫串列化執行。
5.5本章重點回顧
1)Redis提供了兩種持久化方式:RDB和AOF。
2)RDB使用一次性生成內存快照的方式,產生的文件緊湊壓縮比更高,因此讀取RDB恢復速度更快。由於每次生成RDB開銷較大,無法做到實時持久化,一般用於數據冷備和復制傳輸。
3)save命令會阻塞主線程不建議使用,bgsave命令通過fork操作創建子進程生成RDB避免阻塞。
4)AOF通過追加寫命令到文件實現持久化,通過appendfsync參數可以控制實時/秒級持久化。因為需要不斷追加寫命令,所以AOF文件體積逐漸變大,需要定期執行重寫操作來降低文件體積。
5)AOF重寫可以通過auto-aof-rewrite-min-size和auto-aof-rewrite-percentage參數控制自動觸發,也可以使用bgrewriteaof命令手動觸發。
6)子進程執行期間使用-on-write機制與父進程共享內存,避免內存消耗翻倍。AOF重寫期間還需要維護重寫緩沖區,保存新的寫入命令避免數據丟失。
7)持久化阻塞主線程場景有:fork阻塞和AOF追加阻塞。fork阻塞時間跟內存量和系統有關,AOF追加阻塞說明硬碟資源緊張。
8)單機下部署多個實例時,為了防止出現多個子進程執行重寫操作,建議做隔離控制,避免CPU和IO資源競爭。