聲明:本文內容來自《Redis開發與運維》一書第12章,如轉載請聲明。
通 常來看,Redis開發和運維人員更加關注的是Redis本身的一些配置優化,例如AOF和RDB的配置優化、數據結構的配置優化等,但是對於操作系統是 否需要針對Redis做一些配置優化不甚了解或者不太關心,然而事實證明一個良好的系統操作配置能夠為Redis服務良好運行保駕護航。
眾所周知Redis的作者對於Windows操作系統並不感冒,目前大部分公司都會將Web服務器、數據庫服務器等部署在Linux操作系統上,Redis也不例外。所以接下來介紹Linux操作系統如何優化Redis,包含如下七個方面。
一. 內存分配控制
二. swappiness
三. Transparent Huge Pages
四. OOM killer
五. 使用NTP
六. ulimit
七. TCP backlog
一. 內存分配控制
1. vm.overcommit_memory
Redis在啟動時可能會出現這樣的日志:
1 |
# WARNING overcommit_memory is set to 0! Background save may fail under low memory condition. To fix this issue add 'vm.overcommit_memory = 1' to /etc/sysctl.conf and then reboot or run the |
在分析這個問題之前,首先要弄清楚什么是overcommit?Linux操作系統對大部分申請內存的請求都回復yes,以便能運行更多的程序。因為申請內存后,並不會馬上使用內存,這種技術叫做overcommit。如果Redis在啟動時有上面的日志,說明vm.overcommit_memory=0,Redis提示把它設置為1。
vm.overcommit_memory用來設置內存分配策略,它有三個可選值,如下表所示。
vm.overcommit_memory | 含義 |
---|---|
0 | 表示內核將檢查是否有足夠的可用內存。如果有足夠的可用內存,內存申請通過,否則內存申請失敗,並把錯誤返回給應用進程 |
1 | 表示內核允許超量使用內存直到用完為止 |
2 | 表示內核決不過量的(“never overcommit”)使用內存,即系統整個內存地址空間不能超過swap+50%的RAM值,50%是overcommit_ratio默認值,此參數同樣支持修改 |
1 |
注意:本文的可用內存代表物理內存與swap之和。 |
日志中的Background save代表的是bgsave和bgrewriteaof,如果當前可用內存不足,操作系統應該如何處理fork。如果vm.overcommit_memory=0,代表如果沒有可用內存,就申請內存失敗,對應到Redis就是fork執行失敗,在Redis的日志會出現:
1 |
Cannot allocate memory |
Redis建議把這個值設置為1,是為了讓fork能夠在低內存下也執行成功。
2. 獲取和設置
獲取:
1 |
cat /proc/sys/vm/overcommit_memory |
設置:
1 |
echo "vm.overcommit_memory=1" >> /etc/sysctl.conf |
3. 最佳實踐
- Redis設置合理的maxmemory,保證機器有20%~30%的閑置內存。
- 集中化管理aof重寫和rdb的bgsave。
- 設置vm.overcommit_memory=1,防止極端情況下,會造成fork失敗。
二. swappiness
1. 參數說明
swap對於操作系統來比較重要,當物理內存不足時,可以swap out一部分內存頁,已解燃眉之急。但世界上沒有免費午餐,swap空間由硬盤提供,對於需要高並發、高吞吐的應用來說,磁盤IO通常會成為系統瓶頸。在Linux中,並不是要等到所有物理內存都使用完才會使用到swap,系統參數swppiness會決定操作系統使用swap的傾向程度。swappiness的取值范圍是0~100,swappiness的值越大,說明操作系統可能使用swap的概率越高,swappiness值越低,表示操作系統更加傾向於使用物理內存。swap的默認值是60,了解這個值的含義后,有利於Redis的性能優化。下表對swappiness的重要值進行了說明。
swapniess | 策略 |
---|---|
0 | Linux3.5以及以上:寧願OOM killer也不用swap Linux3.4以及更早:寧願swap也不要OOM killer |
1 | Linux3.5以及以上:寧願swap也不要OOM killer |
60 | 默認值 |
100 | 操作系統會主動地使用swap |
1 |
運維提示:OOM(Out Of Memory) killer機制是指Linux操作系統發現可用內存不足時,強制殺死一些用戶進程(非內核進程),來保證系統有足夠的可用內存進行分配。 |
從下表中可以看出,swappiness參數在Linux 3.5版本前后的表現並不完全相同,Redis運維人員在設置這個值需要關注當前操作系統的內核版本。
2. 設置方法
swappiness設置方法如下:
1 |
echo {bestvalue} > /proc/sys/vm/swappiness |
但是上述方法在系統重啟后就會失效,為了讓配置在重啟Linux操作系統后立即生效,只需要在/etc/sysctl.conf追加 vm.swappiness={bestvalue}即可。
1 |
echo vm.swappiness={bestvalue} >> /etc/sysctl.conf |
需要注意/proc/sys/vm/swappiness是設置操作,/etc/sysctl.conf是追加操作。
3. 如何監控swap
(1) 查看swap的總體情況
Linux提供了free命令來查詢操作系統的內存使用情況,其中也包含了swap的相關使用情況。下面是某台Linux服務器執行free –m(以兆為到位)的結果,其中需要重點關注的是最后一行的swap統計,從執行結果看,swap一共有4095M,使用了0M,空閑了4095M。
1 |
total used free shared buffers cached |
在另一台Linux服務器同樣執行free -m,這台服務器開啟了8189M swap,其中使用了5241M。
1 |
total used free shared buffers cached |
(2) 實時查看swap的使用
Linux提供了vmstat命令查詢系統的相關性能指標,其中包含負載、CPU、內存、swap、IO的相關屬性。但其中和swap有關的指標是si和so,它們分別代表了操作系統的swap in和swap out。下面是執行vmstat 1(每隔一秒輸出)的效果,可以看到si和so都為0,代表當前沒有使用swap。
1 |
# vmstat 1 |
(3) 查看指定進程的swap使用情況
Linux操作系統中,/proc/{pid}目錄是存儲指定進程的相關信息,其中/proc/{pid}/smaps是記錄了當前進程所對應的內存映像信息,這個信息對於查詢指定進程的swap使用情況很有幫助。下面以一個Redis實例進行說明
通過info server獲取Redis的進程號process_id:
1 |
redis-cli -h ip -p port info server | grep process_id |
通過cat /proc/986/smaps查詢Redis的smaps信息,由於有多個內存塊信息,這里只輸出一個內存塊鏡像信息進行觀察。
1 |
2aab0a400000-2aab35c00000 rw-p 2aab0a400000 00:00 0 |
其中Swap字段代表該內存塊存在swap分區的數據大小。通過執行如下命令,就可以找到每個內存塊鏡像信息中,這個進程使用到的swap量,通過求和就可以算出總的swap用量。
1 |
cat /proc/986/smaps | grep Swap |
4. 最佳實踐
如果Linux>3.5,vm.swapniess=1,否則vm.swapniess=0,從而實現如下兩個目標:
- 物理內存充足時候,使Redis足夠快。
- 物理內存不足時候,避免Redis死掉(如果當前Redis為高可用,死掉比阻塞更好)。
三. Transparent Huge Pages
Redis在啟動時可能會看到如下日志:
1 |
WARNING you have Transparent Huge Pages (THP) support enabled in your kernel. This will create latency and memory usage issues with Redis. To fix this issue run the command 'echo never > /sys/kernel/mm/transparent_hugepage/enabled' as root, and add it to your /etc/rc.local in order to retain the setting after a reboot. Redis must be restarted after THP is disabled. |
從提示看Redis建議修改Transparent Huge Pages (THP)的相關配置,Linux kernel在2.6.38內核增加了Transparent Huge Pages (THP)特性 ,支持大內存頁(2MB)分配,默認開啟。當開啟時可以降低fork子進程的速度,但fork之后,每個內存頁從原來4KB變為2MB,會大幅增加重寫期間父進程內存消耗。同時每次寫命令引起的復制內存頁單位放大了512倍,會拖慢寫操作的執行時間,導致大量寫操作慢查詢。例如簡單的incr命令也會出現在慢查詢中。因此Redis日志中建議將此特性進行禁用,禁用方法如下:
1 |
echo never > /sys/kernel/mm/transparent_hugepage/enabled |
而且為了使機器重啟后THP配置依然生效,可以在/etc/rc.local中追加echo never > /sys/kernel/mm/transparent_hugepage/enabled。
在設置THP配置時需要注意:有些Linux的發行版本沒有將THP放到/sys/kernel/mm/transparent_hugepage/enabled中,例如Red Hat 6以上的THP配置放到/sys/kernel/mm/redhat_transparent_hugepage/enabled中。而Redis源碼中檢查THP時,把THP位置寫死。
1 |
FILE *fp = fopen("/sys/kernel/mm/transparent_hugepage/enabled","r"); |
所以在發行版中,雖然沒有THP的日志提示,但是依然存在THP所帶來的問題。
1 |
echo never > /sys/kernel/mm/redhat_transparent_hugepage/enabled |
四. OOM killer
OOM killer會在可用內存不足時選擇性的殺掉用戶進程,它的運行規則是怎樣的,會選擇哪些用戶進程“下手”呢?OOM killer進程會為每個用戶進程設置一個權值,這個權值越高,被“下手”的概率就越高,反之概率越低。每個進程的權值存放在/proc/{progress_id}/oom_score中,這個值是受/proc/{progress_id}/oom_adj的控制,oom_adj在不同的Linux版本的最小值不同,可以參考Linux源碼中oom.h(從-15到-17)。當oom_adj設置為最小值時,該進程將不會被OOM killer殺掉,設置方法如下。
1 |
echo {value} > /proc/${process_id}/oom_adj |
對於Redis所在的服務器來說,可以將所有Redis的oom_adj設置為最低值或者稍小的值,降低被OOM killer殺掉的概率。
1 |
for redis_pid in $(pgrep -f "redis-server") |
運維提示:
- 有關OOM killer的詳細細節,可以參考Linux源碼mm/oom_kill.c中oom_badness函數。
- 筆者認為oom_adj參數只能起到輔助作用,合理的規划內存更為重要。
- 通常在高可用情況下,被殺掉比僵死更好,因此不要過多依賴oom_adj配置
五. 使用NTP
NTP(Network Time Protocol)網絡時間協議,一種保證不同機器時鍾一致性的服務。我們知道像Redis Sentinel和Redis Cluster這兩種需要多個Redis實例的類型,可能會涉及多台服務器。雖然Redis並沒有對多個服務器的時鍾有嚴格的要求,但是假如多個Redis實例所在的服務器時鍾不一致,對於一些異常情況的日志排查是非常困難的,例如Redis Cluster的故障轉移,如果日志時間不一致,對於我們排查問題帶來很大的困擾(注:但不會影響集群功能,集群節點依賴各自時鍾)。一般公司里都會有NTP服務用來提供標准時間服務,從而達到糾正時鍾的效果(如下圖所示),為此我們可以每天定時去同步一次系統時間,從而使得集群中的時間是統一。
例如每小時的同步1次NTP服務
1 |
0 * * * * /usr/sbin/ntpdate ntp.xx.com > /dev/null 2>&1 |
六. ulimit
在Linux中,可以通過ulimit查看和設置系統的當前用戶進程的資源數。其中ulimit -a命令包含的open files參數,是單個用戶同時打開的最大文件個數。
1 |
# ulimit –a |
Redis允許同時有多個客戶端通過網絡進行連接,可以通過配置maxclients來限制最大客戶端連接數。對Linux操作系統來說這些網絡連接都是文件句柄。假設當前open files是4096,那么啟動Redis時會看到如下日志。
1 |
# You requested maxclients of 10000 requiring at least 10032 max file descriptors. |
上面的日志解釋如下:
- 第一行:Redis建議把open files至少設置成10032,那么這個10032是如何來的呢?因為maxclients的默認是10000,這些是用來處理客戶端連接的,除此之外,Redis內部會使用最多32個文件描述符,所以這里的10032 = 10000 + 32。
- 第二行:Redis不能將open files設置成10032,因為它沒有權限設置。
- 第三行:當前系統的open files是4096,所以maxclients被設置成4096-32=4064個,如果你想設置更高的maxclients,請使用ulimit -n來設置。
從上面的三行日志分析可以看出open files的限制優先級比maxclients大。
open files的設置方法如下:
1 |
ulimit –Sn {max-open-files} |
七. TCP backlog
Redis默認的tcp-backlog為511,可以通過修改配置tcp-backlog進行調整,如果Linux的tcp-backlog小於Redis設置的tcp-backlog,那么在Redis啟動時會看到如下日志:
1 |
# WARNING: The TCP backlog setting of 511 cannot be enforced because /proc/sys/net/core/somaxconn is set to the lower value of 128. |
查看方法:
1 |
cat /proc/sys/net/core/somaxconn |
修改方法:.
1 |
echo 511 > /proc/sys/net/core/somaxconn |