最近遇到應用程序客戶端在往redis中寫入數據的時候發生了一個這樣一個錯誤:
MISCONF Redis is configured to save RDB snapshots, but is currently not able to persist on disk. Commands that may modify the data set are disabled.
第一次遇到這種錯誤仍是蠻好奇的,主要是它沒法重現(或必現),只是會偶爾出現,客戶端拋出該異常後redis服務狀態也是正常的,特地查了一下相關的資料,結合當時本身的操做場景,還算是解釋的通的。
該問題的大概緣由在於:開啓了snapshot的持久化模式,且在大量寫入的時候bgsave持久化異常,致使客戶端寫入數據失敗,緣由譯文中有說起。
固然解決辦法也很簡單,設置參數stop-writes-on-bgsave-error爲no,也即bgsave異常的時候不要阻止繼續寫入數據。
git
筆者的環境是:centos 7+ Redis 5.0.1;
操做場景是:併發線程以pipline的方式往redis中寫入數據,這樣會頻繁促使發生後臺持久化線程pgsave的工做(操做系統遠大於redis中的數據佔用內存);
異常現象:客戶端會偶發上述錯誤,可是redis服務自己沒有異常,過後能夠正常訪問。
經檢查redis的日誌和系統日誌,均沒有記錄到異常信息,應該是redis設置了stop-writes-on-bgsave-error=yes的狀況下,服務端bgsave失敗以後一種正常的阻止繼續寫入行爲。
同時,儘管在操做系統的內存充足的狀況下,redis的bgsave爲何會失敗,這也是一個須要思考的問題。
這個問題一直沒有查到日誌(不論是redis的日誌仍是系統的日誌),一直懷疑是否是本身整錯什麼東西了,直至發現這個哥們也提到這個問題:the issue happens intermittently and dont know why. no indication in the logs.github
另:redis的bgsave是一個獨立於redis服務的進程,對於在大批量寫入的狀況下,常常能夠看到一個redis實例下的這個bgsave獨立進程(進程之間是沒法共享內存的)
redis
如下爲譯文,原文地址:https://blog.sakuragawa.moe/debugging-misconf-redis-is-configured-to-save-rdb-snapshots/數據庫
有時候Redis會拋出以下的錯誤:centos
MISCONF Redis is configured to save RDB snapshots, but is currently not able to persist on disk. Commands that may modify the data set are disabled. Please check Redis logs for details about the error.
發生此錯誤是由於BGSAVE失敗。不少時候BGSAVE失敗是由於fork沒法分配內存。不少時候fork沒法分配內存(儘管機器有足夠的可用RAM),由於操做系統的優化衝突。服務器
Override Error in Redis Configuration併發
使用redis cli,您能夠中止嘗試保存快照:app
config set stop-writes-on-bgsave-error no
這是一個快速的解決方法,可是若是您關心使用它的數據,您應該首先檢查一下BGSAVE失敗的緣由。
這就暫時解決了這個問題。可是,過分查看這個錯誤是一種可怕的方法,由於這個選項的做用是阻止redis通知寫操做已經中止,而且在不將數據寫入快照的狀況下繼續操做。這只是忽略了這個錯誤。dom
Save Redis on Low Memoryide
因爲內存不足,bgsave過程當中可能出現錯誤。
發生此錯誤是由於BGSAVE失敗。在BGSAVE期間,Redis fork一個子進程以將數據保存到磁盤上。
雖然BGSAVE失敗的確切緣由能夠從日誌中查看(一般在/var/log/redis/redis-服務器日誌可是不少時候BGSAVE失敗是由於fork不能分配內存。不少時候fork沒法分配內存(儘管機器有足夠的可用RAM),由於操做系統的優化衝突。
從Redis常見問題中能夠看出:
Background saving is failing with a fork() error under Linux even if I've a lot of free RAM!
Redis後臺保存模式依賴於現代操做系統中fork的copy-on-write語義:Redis forks(建立一個子進程)是父進程的徹底副本。子進程將數據庫轉儲到磁盤上,最後退出。
理論上,子進程應該使用與父進程做爲副本同樣多的內存,但實際上因爲大多數現代操做系統實現的「寫時拷貝」語義,父進程和子進程將共享公共內存頁。
只有在子級或父級中更改頁時,纔會複製該頁。由於理論上,當子進程保存時,全部的頁面均可能會發生變化,因此Linux沒法預先知道子進程將佔用多少內存,所以,若是overcommit_memory設置設置爲zero fork,則將失敗,除非有足夠多的可用RAM來真正複製全部父內存頁,結果是,若是你有一個3gb的Redis數據集,而只有2gb的空閒內存,那麼它將失敗。
# echo 'vm.overcommit_memory = 1' >> /etc/sysctl.conf # sysctl vm.overcommit_memory=1
Redis不須要操做系統認爲的那樣多的內存來寫入磁盤,所以可能會先發制人地讓fork失敗。