在Redis的開發(fā)和運維過程中,由于對于Redis的某些特性沒有真正合理地使用,會遇到一些棘手的問題,本章將對一些典型的“陷阱”進行逐一分析并提出解決方案,主要內(nèi)容包括:
Linux配置優(yōu)化要點。
flushall/flushdb誤操作快速恢復(fù)方法。
安全的Redis如何設(shè)計。
處理bigkey的方案與最佳實踐。
尋找熱點key。
Linux配置優(yōu)化
通常來看,Redis開發(fā)和運維人員更加關(guān)注的是Redis本身的一些配置優(yōu)化,例如AOF和RDB的配置有優(yōu)化、數(shù)據(jù)結(jié)構(gòu)的配置優(yōu)化等,但是對于操作系統(tǒng)是否需要對Redis做一些配置優(yōu)化不甚了解或者不太關(guān)心。然而事實證明一個良好的系統(tǒng)操作配置能夠為Redis服務(wù)良好運行保駕護航。
在第一章我們提到過,Redis的作者對于Window操作系統(tǒng)并不感興趣,目前大部分公司都會將Web服務(wù)器、數(shù)據(jù)庫等服務(wù)器部署在Linux操作系統(tǒng)上,Redis也不例外,所以接下來介紹Linux操作系統(tǒng)如何優(yōu)化Redis。
-
內(nèi)存分配控制
-
vm.overcommit_memory
在分析這個問題之前,首先要弄清楚什么是overcommit?Linux操作系統(tǒng)對大部分申請內(nèi)存的請求都回復(fù)yes,以便能運行更多的程序。因為申請內(nèi)存后,并不會馬上使用內(nèi)存,這種技術(shù)叫做overcommit。如果Redis在啟動時有上面的日志,說明vm.overcommit_memory=0,Redis提示把它設(shè)置為1。
vm.overcommit_memory用來設(shè)置內(nèi)存分配策略,有三個可選值,如下表所示:
值 含義 0 表示內(nèi)核將檢查是否有足夠的可用內(nèi)存。如果有足夠的可用內(nèi)存,內(nèi)存申請通過,否則內(nèi)存申請失敗,并把錯誤返回給應(yīng)用進程 1 表示內(nèi)核允許超量使用內(nèi)存直到用完為止 2 表示內(nèi)核決不過量的(“never overcommit”)使用內(nèi)存,即系統(tǒng)整個內(nèi)存地址空間不能超過swap+50%的RAM值,50%是overcommit_ratio默認(rèn)值,此參數(shù)同樣支持修改 注意:本節(jié)的可用內(nèi)存代表物理內(nèi)存與swap之和。
日志中的Background save代表的是bgsave和bgrewriteaof,如果當(dāng)前可用內(nèi)存不足,操作系統(tǒng)應(yīng)用如何處理fork操作。如果vm.overcommit_memory=0,代表如果沒有可用內(nèi)存,就申請內(nèi)存失敗,對應(yīng)到Redis就是執(zhí)行fork失敗,在Redis的日志會出現(xiàn):
Cannot allocate memory
Redis建議把這個值設(shè)置為1,是為了讓fork操作能夠在低內(nèi)存下也執(zhí)行成功。
-
獲取和設(shè)置
獲取:
# cat /proc/sys/vm/overcommit_memory =
設(shè)置:
echo "vm.overcommit_memory=1" >> /etc/sysctl.conf sysctl vm.overcommit_memory=1
-
最佳實踐
Redis設(shè)置合理的maxmemory,保證機器有20%~30%的閑置內(nèi)存。
集中化管理AOF重寫和RDB的bgsave
設(shè)置vm.overcommit_memory=1,防止極端情況下回造成fork失敗。
-
-
swappiness
-
參數(shù)說明
swap對于操作系統(tǒng)比較重要,當(dāng)物理內(nèi)存不足時,可以將一部分內(nèi)存頁進行swap操作,以解燃眉之急。但世界上沒有免費午餐,swap空間由硬盤提供,對于需要高并發(fā)、高吞吐的應(yīng)用來說,磁盤IO通常會成為系統(tǒng)瓶頸。在Linux中,并不是要等到所有物理內(nèi)存都是用完才會使用到swap,系統(tǒng)參數(shù)swappiness會決定操作系統(tǒng)使用swap的傾向程序。swappiness的取值范圍是0~100,swappiness的值越大,說明操作系統(tǒng)可能使用swap的概率越高,swappiness值越低,表示操作系統(tǒng)更加傾向于適用于物理內(nèi)存。swap的默認(rèn)值是60,了解這個值的含義后,有利于Redis的性能優(yōu)化。下表對swappiness的重要值進行了說明。
值 策略 0 Linux3.5以及以上:寧愿用OOM killer也不用swap,Linux3.4以及更早反之。 1 Linux3.5以及以上:寧愿用swap也不用OOM killer 60 默認(rèn)值 100 操作系統(tǒng)會主動地使用swap 運維提示:OOM(Out Of Memory)killer機制是值Linux操作系統(tǒng)發(fā)現(xiàn)可用內(nèi)存不足時,強制殺死一些用戶進行(非內(nèi)核進程),來保證系統(tǒng)有足夠的可用內(nèi)存進行分配。
從上表可以看出,swappiness參數(shù)在Linux3.5版本前后的表現(xiàn)并不完全系統(tǒng),Redis運維人員在設(shè)置這個值需要關(guān)注當(dāng)前操作系統(tǒng)的內(nèi)核版本。
-
設(shè)置方法
swappiness設(shè)置方法如下:
echo {bestvalue} > /proc/sys/vm/swappiness
但是上述方法在系統(tǒng)重啟后就會失效,為了讓配置在重啟Linux操作系統(tǒng)后立即生效,只需要在/etc/sysctl.conf追加vm.swappiness={bestvalue}即可。
echo vm.swappiness={bestvalue} >> /etc/sysctl.conf
需要注意/proc/sys/vm/swappiness是設(shè)置操作,/etc/sysctl.conf是追加操作。
-
如何監(jiān)控swap
(1)查看swap的總體情況
Linux提供了free命令來查詢操作系統(tǒng)的內(nèi)存使用情況,其中也包含了swap的相關(guān)使用情況。
(2)實時查看swap的使用
Linux提供了vmstat命令查詢系統(tǒng)的相關(guān)性能指標(biāo),其中包含負(fù)載、CPU、內(nèi)存、swap、IO的相關(guān)屬性。但其中和swap有關(guān)的指標(biāo)是si和so,它們分別代表操作系統(tǒng)的swap in和swap out。下面是執(zhí)行vmstat 1(每隔一秒輸出)的效果,可以看到si和so都為0,代表當(dāng)前沒有使用swap。
(3)查看指定進程的swap使用情況
Linux操作系統(tǒng)中,/proc/{pid}目錄是存儲指定進程的相關(guān)信息,其中/proc/{pid}/smaps記錄了當(dāng)前進程所對應(yīng)的內(nèi)存映像信息,這個信息對于查詢指定進程的swap使用情況很有幫助。如果Linux>3.5,vm.swapniess=1,否則vm.swapniess=0,從而實現(xiàn)如下兩個目標(biāo):
物理內(nèi)存充足時候,使Redis足夠快。
物理內(nèi)存不足時候,避免Redis死掉(如果當(dāng)前Redis為高可用,死掉比阻塞更好)。
-
-
THP
Redis啟動時日志中建議修改Transparent Huge Pages(THP)的相關(guān)配置,Linux kernel在2.6.38內(nèi)核增加了THP特性,支持大內(nèi)存也(2MB)分配,默認(rèn)開啟。當(dāng)開啟時可以加快fork子進程的速度,但fork操作之后,每個內(nèi)存頁從原來4KB變?yōu)?KB,會大幅增加重寫期間父進程內(nèi)存消耗。同時每次寫命令引起的復(fù)制內(nèi)存頁單位放大了512倍,會拖慢寫操作的執(zhí)行時間,導(dǎo)致大量寫操作慢查詢,例如簡單的incr命令也會出現(xiàn)在慢查詢中。因此Redis氣質(zhì)中建議將此特性進行禁用,禁用方法如下:
echo never > /sys/kernel/mm/transparent_hugepage/enabled
為了使機器重啟后THP配置依然生效,可以在/etc/rc.local中追加echo never > /sys/kernel/mm/transparen_hugepage/enablec。
在設(shè)置THP配置時需要注意:有些Linux的發(fā)行版本沒有將THP當(dāng)?shù)?sys/kernel/mm/transparent_hugepage/enabled中,例如Red Hat 6以上的THP配置放到/sys/kernel/mm/redhat_transparent_hugepage/enabled中。而Redis源碼中檢查THP時,把THP位置寫死:
FILE * fp = fopen("/sys/kernel/mm/transparent_hugepage/enabled", "r"); if (!fp) return 0;
所以在發(fā)行版中,雖然沒有THP的日志提示,但是依然存在THP所帶來的問題:
echo never > /sys/kernel/mm/redhat_transparent_hugepage/enabled
-
OOM killer
OOM killer會在可用內(nèi)存不足時選擇性地殺掉用戶進程,它的運行規(guī)則是怎樣的,會選擇哪些用戶進程“下手”呢?OOM killer進程會為每個用戶進程設(shè)置一個權(quán)值,這個權(quán)值越高,被“下手”的概率就越高,反之概率越低。每個進程的權(quán)值存放在/proc/{progress_id}/oom_score中,這個值是受/proc/{progress_id}/oom_adj的控制,oom_adj在不同的Linux版本中最小值不同,可以參考Linux源碼中oom.h(從 -15到-17)。當(dāng)oom_adj設(shè)置為最小值是,該進程將不會被OOM killer殺掉,設(shè)置方法如下:
echo {value} > /proc/${process_id}/oom_adj
對于Redis所在的服務(wù)器來說,可以將所有Redis的oom_adj設(shè)置為最低值或者稍小的值,降低被OOM killer殺掉的概率:
for redis_pid in $(pgrep -f "redis-server") do echo -17 > /proc/${redis_pid}/oom_adj done
-
使用NTP
NTP(Network Time Protocol,網(wǎng)絡(luò)時間協(xié)議)是一種保證不同機器始終一致性的服務(wù)。向Redis Sentinel和Redis Cluster這兩種功能需要多個Redis節(jié)點的類型,可能會涉及多臺服務(wù)器。雖然Redis并沒有對多個服務(wù)器的時鐘有嚴(yán)格要求,但是假如多個Redis實例所在的服務(wù)器時鐘不一致,對于一些異常情況的日志排查是非常困難的,例如Redis Cluster的故障轉(zhuǎn)移,如果日志時間不一致,對于我們排查問題帶來很大的困擾(注:但不會影響集群功能,集群節(jié)點依賴各自時鐘)。一般公司里都會有NTP服務(wù)用來提供標(biāo)準(zhǔn)時間服務(wù),從而達到糾正時鐘的效果,為此我們可以每天定時去同步一次系統(tǒng)時間,從而使得集群中的時間保持統(tǒng)一。
-
ulimit
在Linux中,可以通過ulimit查看和設(shè)置系統(tǒng)當(dāng)前用戶進程的資源數(shù)。其中ulimit -a命令包含的open files參數(shù),是單個用戶同時打開的最大文件個數(shù)。
Redis允許同時有多個客戶端通過網(wǎng)絡(luò)進程連接,可以通過配置maxclients來限制最大客戶端連接數(shù)。對Linux操作系統(tǒng)來說,這些網(wǎng)絡(luò)連接都是文件句柄。
-
TCP backlog
Redis默認(rèn)的tcp-backlog值為511,可以通過修改配置tcp-backlog進行調(diào)整。
查看方法:
# cat /proc/sys/net/core/somaxconn 128
修改方法:
echo 511 > /proc/sys/net/core/somaxconn