問題描述git
業務反饋正常有個接口正常訪問在100ms之內,有時候調用時長會要10多s,根據業務提供的時間查redis日誌,以現有以下記錄:github
8788:M 24 Aug 01:21:26.008 * 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. 8788:M 24 Aug 01:21:45.006 * 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 aof相關配置redis
127.0.0.1:6390> config get *append* 1) "no-appendfsync-on-rewrite" 2) "yes" 3) "appendfsync" 4) "everysec" 5) "appendonly" 6) "yes"
查看rdb的配置:bash
127.0.0.1:6390> config get save 1) "save" 2) ""
查看redis的版本:服務器
127.0.0.1:6390> info server # Server redis_version:3.2.4
故障分析app
打開AOF持久化功能後, Redis處理完每一個事件後會調用write(2)將變化寫入kernel的buffer,若是此時write(2)被阻塞,Redis就不能處理下一個事件。async
Linux規定執行write(2)時,若是對同一個文件正在執行fdatasync(2)將kernel buffer寫入物理磁盤,或者有system wide sync在執行,write(2)會被Block住,整個Redis被Block住。ide
若是系統IO繁忙,好比有別的應用在寫盤,或者Redis本身在AOF rewrite或RDB snapshot(雖然此時寫入的是另外一個臨時文件,雖然各自都在連續寫,但兩個文件間的切換使得磁盤磁頭的尋道時間加長),就可能致使fdatasync(2)遲遲未能完成從而Block住write(2),Block住整個Redis。post
爲了更清晰的看到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秒的數據丟失。
解決方法
方法一:關閉aof
這個方法須要和業務確認是否可行,我的認爲若是採用redis主從+sentinel方式的話主節點掛了從節點會本身提高爲主點,主節點恢復後全量同步一次數據就能夠了,關係也不是太大
方法二:修改系統配置
原來是AOF rewrite時一直埋頭的調用write(2),由系統本身去觸發sync。在RedHat Enterprise 6裏,默認配置vm.dirty_background_ratio=10,也就是佔用了10%的可用內存纔會開始後臺flush,而個人服務器有8G內存。
很明顯一次flush太多數據會形成阻塞,因此最後果斷設置了sysctl vm.dirty_bytes=33554432(32M),問題解決。
而後提了個issue,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
嘗試修改一下配置文件/etc/sysctl.conf,並使配置當即生效
echo "vm.dirty_bytes=33554432" >> /etc/sysctl.conf sysctl -p
驗證修改是否成功
>sysctl -a | grep vm.dirty_bytes vm.dirty_bytes = 33554432
參考:
https://ningyu1.github.io/site/post/32-redis-aof/
https://redis.io/topics/latency