redis 持久化 AOF和 RDB 引發的生產故障

概要
       最近聽開發的同事說,應用程序鏈接 redis 時老是拋出鏈接失敗或超時之類的錯誤。經過觀察在 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." 頻率極其頻繁。百度了一番,提示該錯誤可能會形成 redis server 上的處理延遲,並且客戶端上也可能發生異常或鏈接超時。redis

       繼續深刻發現,這是由於啓用了 AOF(appendonly yes),而且 appendfsync 參數爲 everysec,形成的磁盤 I/O 負載高。那麼本文將介紹該問題發生的緣由,並給出一些建議。緩存

故障分析bash

       首先咱們要知道,redis 是一個多路複用的單進程應用程序。多路,指的是多個網絡地址,複用是指重複利用單個線程。當打開 AOF 持久化功能後, Redis 處理完每一個事件後會調用 write(2) 將變化寫入 kernel 的 buffer,若是此時 write(2) 被阻塞,Redis 就不能處理下一個事件。Linux 規定執行 write(2) 時,若是對同一個文件正在執行fdatasync(2)將 kernel buffer寫入物理磁盤,或者有system wide sync在執行,write(2)會被Block住,整個Redis被Block住。服務器

       若是系統IO 繁忙,好比有別的應用在寫盤,或者Redis本身在AOF rewrite或RDB snapshot(雖然此時寫入的是另外一個臨時文件,雖然各自都在連續寫,但兩個文件間的切換使得磁盤磁頭的尋道時間加長),就可能致使 fdatasync(2) 遲遲未能完成從而 Block 住 write(2),Block 住整個 Redis。而,咱們的生產主庫和從庫都同時開啓了AOF已經RDB save的方式作持久化。網絡

       爲了更清晰的看到fdatasync(2)的執行時長,可使用 "strace -p (pid of redis server) -T -e -f trace=fdatasync",但會影響系統性能。Redis提供了一個自救的方式,當發現文件有在執行 fdatasync(2) 時,就先不調用 write(2),只存在 cache 裏,省得被 Block。但若是已經超過兩秒都仍是這個樣子,則會硬着頭皮執行 write(2),即便 redis 會被 Block 住。此時那句要命的 log 會打印:"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."。以後用redis-cli INFO 能夠看到 aof_delayed_fsync 的值被加1。所以,對於 fsync 設爲 everysec 時丟失數據的可能性的最嚴謹說法是:若是有 fdatasync 在長時間的執行,此時 redis 意外關閉會形成文件裏很少於兩秒的數據丟失。若是 fdatasync 運行正常,redis 意外關閉沒有影響,只有當操做系統 crash 時纔會形成少於1秒的數據丟失。架構

       網上有小夥伴說,該錯誤在後續版本有修復。我我的這並非一個BUG,至於真假還請小夥伴驗證。app

解決方法async

方法1、各類修改配置ide

最後發現,原來是AOF rewrite時一直埋頭的調用 write(2),由系統本身去觸發 sync。在RedHat Enterprise 6裏,默認配置vm.dirty_background_ratio=10,也就是佔用了 10% 的可用內存纔會開始後臺 flush,而個人服務器有 8G 內存。很明顯一次 flush 太多數據會形成阻塞,因此最後果斷設置 "sysctl vm.dirty_bytes=33554432(32M)" 。AOF rewrite時定時也執行一下 fdatasync 嘛, antirez 回覆新版中,AOF rewrite 時 32M 就會重寫主動調用 fdatasync。性能

查看一下系統內核參數

>sysctl -a | grep dirty_background_ratio
vm.dirty_background_ratio = 10

>sysctl -a | grep vm.dirty_bytes
vm.dirty_bytes = 0

嘗試修改

echo "vm.dirty_bytes=33554432" >> /etc/sysctl.conf  

最後執行 "sysctl -p" 使配置生效;

方法2、關閉 RDB 或 AOF 持久化

經過上面咱們知道該故障問題,其實就是由於磁盤大量IO 形成的請求阻塞。那麼禁用 RDB 或 AOF 任一持久化便可,固然,建議禁用 RDB 而使用 AOF。

最後

下面再給你們一些在實際生產中的建議,以及避免這類問題的發生。首先,若是架構容許儘可能不要在主庫上同時作 RBD 和 AOF 持久化(若是要作,就使用SSD固態硬盤)。可是,這樣若是不作持久化,將暴露以下問題:

  1. 當 redis 服務掛掉以後,重啓緩存所有丟失;
  2. 當主機掛掉,經過腳本或sentinel將從提高爲主以後,之前主庫就做廢;

這樣作的好處就是,提高了主庫的性能。壞處就是,當主庫宕機以後,你只能將該主機從主從當中剔除;

上面這種方式提升了必定的複雜性,那麼要解決這個問題。那麼能夠選擇一個折中辦法,那就是作AOF持久化而不使用RDB的方式。咱們能夠經過下面的內容來選擇適合本身的持久化方式,以下;

  RDB。在這裏,您能夠根據您配置的保存觸發器進行大量權衡。
  AOF + fsync 老是這樣:這個速度很是慢,只有當你知道你在作什麼的時候才應該使用它。
  AOF + fsync 每一秒:這是一個很好的妥協。
  AOF + fsync 每秒+ no-appendfsync-on-rewrite選項設置爲yes:如上所示,但避免在重寫期間fsync下降磁盤壓力。
  AOF + fsync 永遠不會。 Fsyncing在這個設置上取決於內核,甚至更少的磁盤壓力和延遲尖峯的風險。

至於在工做當中,如何選擇,嘿嘿,那就只能結合開發需求和部門領導意見啦。

相關文章
相關標籤/搜索