機器配置:2 CPU,8GB 內存
需要預先安裝 sysstat 等工具,如 yum install sysstat
終端中運行 free 命令,查看 Swap 的使用情況。
$ free total used free shared buff/cache available Mem: 8169348 331668 6715972 696 1121708 7522896 Swap: 0 0 0
從這個 free 輸出你可以看到,Swap 的大小是 0,這說明我的機器沒有配置 Swap。為了繼續 Swap 的案例, 就需要先配置、開啟 Swap。如果你的環境中已經開啟了 Swap,那你可以略過下面的開啟步驟,繼續往后走。要開啟 Swap,我們首先要清楚,Linux 本身支持兩種類型的 Swap,即 Swap 分區和 Swap 文件。以 Swap 文件為例,在第一個終端中運行下面的命令開啟 Swap,這里配置 Swap 文件的大小為 8GB:
# 創建Swap文件 $ fallocate -l 8G /mnt/swapfile # 修改權限只有根用戶可以訪問 $ chmod 600 /mnt/swapfile # 配置Swap文件 $ mkswap /mnt/swapfile # 開啟Swap $ swapon /mnt/swapfile
再執行 free 命令,確認 Swap 配置成功:
$ free total used free shared buff/cache available Mem: 8169348 331668 6715972 696 1121708 7522896 Swap: 8388604 0 8388604
現在,free 輸出中,Swap 空間以及剩余空間都從 0 變成了 8GB,說明 Swap 已經正常開啟。接下來,在第一個終端中,運行下面的 dd 命令,模擬大文件的讀取:
# 寫入空設備,實際上只有磁盤的讀請求 $ dd if=/dev/sda1 of=/dev/null bs=1G count=2048
接着,在第二個終端中運行 sar 命令,查看內存各個指標的變化情況。
# 間隔1秒輸出一組數據 # -r表示顯示內存使用情況,-S表示顯示Swap使用情況 $ sar -r -S 1 04:39:56 kbmemfree kbavail kbmemused %memused kbbuffers kbcached kbcommit %commit kbactive kbinact kbdirty 04:39:57 6249676 6839824 1919632 23.50 740512 67316 1691736 10.22 815156 841868 4 04:39:56 kbswpfree kbswpused %swpused kbswpcad %swpcad 04:39:57 8388604 0 0.00 0 0.00 04:39:57 kbmemfree kbavail kbmemused %memused kbbuffers kbcached kbcommit %commit kbactive kbinact kbdirty 04:39:58 6184472 6807064 1984836 24.30 772768 67380 1691736 10.22 847932 874224 20 04:39:57 kbswpfree kbswpused %swpused kbswpcad %swpcad 04:39:58 8388604 0 0.00 0 0.00 … 04:44:06 kbmemfree kbavail kbmemused %memused kbbuffers kbcached kbcommit %commit kbactive kbinact kbdirty 04:44:07 152780 6525716 8016528 98.13 6530440 51316 1691736 10.22 867124 6869332 0 04:44:06 kbswpfree kbswpused %swpused kbswpcad %swpcad 04:44:07 8384508 4096 0.05 52 1.27
sar 的輸出結果是兩個表格,第一個表格表示內存的使用情況,第二個表格表示 Swap 的使用情況。其中,各個指標名稱前面的 kb 前綴,表示這些指標的單位是 KB。
大部分指標我們都已經見過了,剩下的幾個新出現的指標,簡單介紹一下。
kbcommit,表示當前系統負載需要的內存。它實際上是為了保證系統內存不溢出,對需要內存的估計值。%commit,就是這個值相對總內存的百分比。
kbactive,表示活躍內存,也就是最近使用過的內存,一般不會被系統回收。
kbinact,表示非活躍內存,也就是不常訪問的內存,有可能會被系統回收。
清楚了界面指標的含義后,再結合具體數值,來分析相關的現象。可以清楚地看到,總的內存使用率(%memused)在不斷增長,從開始的 23% 一直長到了 98%,並且主要內存都被緩沖區(kbbuffers)占用。具體來說:
剛開始,剩余內存(kbmemfree)不斷減少,而緩沖區(kbbuffers)則不斷增大,由此可知,剩余內存不斷分配給了緩沖區。
一段時間后,剩余內存已經很小,而緩沖區占用了大部分內存。這時候,Swap 的使用開始逐漸增大,緩沖區和剩余內存則只在小范圍內波動。
還得看看進程緩存的情況。cachetop 正好能滿足這一點。
在第二個終端中,按下 Ctrl+C 停止 sar 命令,然后運行下面的 cachetop 命令,觀察緩存的使用情況:
$ cachetop 5 12:28:28 Buffers MB: 6349 / Cached MB: 87 / Sort: HITS / Order: ascending PID UID CMD HITS MISSES DIRTIES READ_HIT% WRITE_HIT% 18280 root python 22 0 0 100.0% 0.0% 18279 root dd 41088 41022 0 50.0% 50.0%
通過 cachetop 的輸出,dd 進程的讀寫請求只有 50% 的命中率,並且未命中的緩存頁數(MISSES)為 41022(單位是頁)。這說明,正是案例開始時運行的 dd,導致了緩沖區使用升高。
這種情況,還得進一步通過 /proc/zoneinfo ,觀察剩余內存、內存閾值以及匿名頁和文件頁的活躍情況。
可以在第二個終端中,按下 Ctrl+C,停止 cachetop 命令。然后運行下面的命令,觀察 /proc/zoneinfo 中這幾個指標的變化情況:
# -d 表示高亮變化的字段 # -A 表示僅顯示Normal行以及之后的15行輸出 $ watch -d grep -A 15 'Normal' /proc/zoneinfo Node 0, zone Normal pages free 21328 min 14896 low 18620 high 22344 spanned 1835008 present 1835008 managed 1796710 protection: (0, 0, 0, 0, 0) nr_free_pages 21328 nr_zone_inactive_anon 79776 nr_zone_active_anon 206854 nr_zone_inactive_file 918561 nr_zone_active_file 496695 nr_zone_unevictable 2251 nr_zone_write_pending 0
可以發現,剩余內存(pages_free)在一個小范圍內不停地波動。當它小於頁低閾值(pages_low) 時,又會突然增大到一個大於頁高閾值(pages_high)的值。'
再結合剛剛用 sar 看到的剩余內存和緩沖區的變化情況,我們可以推導出,剩余內存和緩沖區的波動變化,正是由於內存回收和緩存再次分配的循環往復。當剩余內存小於頁低閾值時,系統會回收一些緩存和匿名內存,使剩余內存增大。其中,緩存的回收導致 sar 中的緩沖區減小,而匿名內存的回收導致了 Swap 的使用增大。緊接着,由於 dd 還在繼續,剩余內存又會重新分配給緩存,導致剩余內存減少,緩沖區增大。
其實還有一個有趣的現象,如果多次運行 dd 和 sar,可能會發現,在多次的循環重復中,有時候是 Swap 用得比較多,有時候 Swap 很少,反而緩沖區的波動更大。換句話說,系統回收內存時,有時候會回收更多的文件頁,有時候又回收了更多的匿名頁。顯然,系統回收不同類型內存的傾向,似乎不那么明顯。你應該想到了上節課提到的 swappiness,正是調整不同類型內存回收的配置選項。
還是在第二個終端中,按下 Ctrl+C 停止 watch 命令,然后運行下面的命令,查看 swappiness 的配置:
$ cat /proc/sys/vm/swappiness 60
swappiness 顯示的是默認值 60,這是一個相對中和的配置,所以系統會根據實際運行情況,選擇合適的回收類型,比如回收不活躍的匿名頁,或者不活躍的文件頁
到這里,已經找出了 Swap 發生的根源
還是推薦 proc 文件系統,用來查看進程 Swap 換出的虛擬內存大小,它保存在 /proc/pid/status 中的 VmSwap 中(推薦你執行 man proc 來查詢其他字段的含義)。
第二個終端中運行下面的命令,就可以查看使用 Swap 最多的進程。
# 按VmSwap使用量對進程排序,輸出進程名稱、進程ID以及SWAP用量 $ for file in /proc/*/status ; do awk '/VmSwap|Name|^Pid/{printf $2 " " $3}END{ print ""}' $file; done | sort -k 3 -n -r | head dockerd 2226 10728 kB docker-containe 2251 8516 kB snapd 936 4020 kB networkd-dispat 911 836 kB polkitd 1004 44 kB
從這里你可以看到,使用 Swap 比較多的是 dockerd 和 docker-containe 進程,所以,當 dockerd 再次訪問這些換出到磁盤的內存時,也會比較慢。這也說明了一點,雖然緩存屬於可回收內存,但在類似大文件拷貝這類場景下,系統還是會用 Swap 機制來回收匿名內存,而不僅僅是回收占用絕大部分內存的文件頁。最后,如果你在一開始配置了 Swap,不要忘記在案例結束后關閉。你可以運行下面的命令,關閉 Swap:
$ swapoff -a
實際上,關閉 Swap 后再重新打開,也是一種常用的 Swap 空間清理方法,比如:
$ swapoff -a && swapon -a
在內存資源緊張時,Linux 會通過 Swap ,把不常訪問的匿名頁換出到磁盤中,下次訪問的時候再從磁盤換入到內存中來。你可以設置 /proc/sys/vm/min_free_kbytes,來調整系統定期回收內存的閾值;也可以設置 /proc/sys/vm/swappiness,來調整文件頁和匿名頁的回收傾向。當 Swap 變高時,你可以用 sar、/proc/zoneinfo、/proc/pid/status 等方法,查看系統和進程的內存使用情況,進而找出 Swap 升高的根源和受影響的進程。反過來說,通常,降低 Swap 的使用,可以提高系統的整體性能。也總結了幾種常見的降低方法。禁止 Swap,現在服務器的內存足夠大,所以除非有必要,禁用 Swap 就可以了。隨着雲計算的普及,大部分雲平台中的虛擬機都默認禁止 Swap。如果實在需要用到 Swap,可以嘗試降低 swappiness 的值,減少內存回收時 Swap 的使用傾向。響應延遲敏感的應用,如果它們可能在開啟 Swap 的服務器中運行,你還可以用庫函數 mlock() 或者 mlockall() 鎖定內存,阻止它們的內存換出。