原文:http://blog.jobbole.com/52898/linux
本文由伯樂在線-高磊翻譯自Metin Doslu。歡迎加入技術翻譯小組。轉載請參見文章末尾處的要求。
算法
聲明:咱們經常覺得,一旦咱們(的代碼)出了什麼情況,那確定是操做系統在做祟,而在99%的狀況下,結果都會是別的緣由。所以咱們會謹慎地做出是操做系統致使了某個問題這樣的假設,除非你遇到了與下面的例子相似的狀況。
一切從咱們的一個客戶報告了他們的CitusDB集羣的性能問題開始。這個客戶設計的集羣使得他們的工做數據集合能夠放進內存,可是他們的查詢次數顯示他們的查詢已經須要訪問磁盤。這天然會致使查詢效率降低10倍到100倍。
咱們開始着手研究這個問題,首先檢查CitusDB的查詢分發機制,而後再檢查機器上安裝的PostgreSQL實例。發現都不是致使該問題出現的緣由。接下來的一些發現:
客戶的工做數據是某一天的查詢日誌。一旦他們看完了某一天的數據,他們會開始查詢下一天的數據。
他們的查詢大都是連續的I/O操做,使用索引的狀況並很少。
某一天的數據會佔用一個節點超過60%的內存(但仍是大大小於整個可用的內存)。他們實例上沒有別的使用內存的程序。
咱們假設,由於每一天的數據能夠容易的放進內存,Linux 內存管理器最終會把那一天的數據都放進頁緩存,一旦客戶開始查詢下一天的日誌時,新的數據會進入頁緩存,至少,這是一個使用LRU退化策略的簡單緩存(管理器)會作的事情。
可是LRU在用做頁替換策略算法時有兩個缺陷。第一,精確的LRU實如今一個系統環境下成本過高了;第二,內存管理器還須要把數據使用的頻率考慮在內,讀入一 個大文件時並不會立刻清除整個cache,所以。Linux使用了比 LRU 更復雜的算法,而這個算法與咱們以前描述過的問題協做的效果並很差。
舉例說明。假設你的內核版本號高於2.6.31 ,而你在使用一個內存爲68GB的EC2集羣,好比你有兩天的點擊流數據。每一天的數據都能超過60%的總的內存,單個來看,都很容易能放進內存。
$ ls-lh clickstream.csv.*
-rw-rw-r-- ec2-user ec2-user 42G Nov 25 19:45 clickstream.csv.1
-rw-rw-r-- ec2-user ec2-user 42G Nov 25 19:47 clickstream.csv.2
如今,咱們經過對點擊流文件運行屢次 wc 命令來將該天的數據裝進內存。
注意這兩次所用的時間差。
第一次咱們運行該命令時,Linux內存管理器會將該文件頁放進頁緩存,下一次運行時,會直接從內存裏面讀取。
$ timewc-l clickstream.csv.1
336006288 clickstream.csv.1
real 10m4.575s
...
$ timewc-l clickstream.csv.1
336006288 clickstream.csv.1
real 0m18.858s
如今咱們切換到次日的點擊流文件。咱們再屢次運行 wc 命令來把文件裝進內存。使用一個類LRU的策略會將第一天的數據淘汰,並將次日的數據裝進內存。不幸的是,在這種狀況下,無論你運行多少次,Linux 內存管理器都不會把次日的數據裝進內存。
$ timewc-l clickstream.csv.2
336027448 clickstream.csv.2
real 9m50.542s
$ timewc-l clickstream.csv.2
336027448 clickstream.csv.2
real 9m52.265s
事實上,若是你遇到這種狀況,惟一能把次日的數據裝進內存的辦法就是手動清除掉頁緩存,很明顯,這個作法會比問題帶來的危害更大,但單就咱們的這個小測試而言,確實湊效了。
$ echo1 | sudotee/proc/sys/vm/drop_caches
1
$ timewc-l clickstream.csv.2
336027448 clickstream.csv.2
real 9m51.906s
$ timewc-l clickstream.csv.2
336027448 clickstream.csv.2
real 0m17.874s
回到上一步,這兒的問題在於Linux如何管理本身的頁緩存。Linux內存管理器會將文件系統的頁面放到兩種類型的隊列裏面。一個隊列(臨近訪問內存隊列,下面簡稱:臨近隊列)放了最近訪問到的頁面。另外一個隊列(頻率訪問內存隊列,下面簡稱:頻率隊列)保留了那些被屢次訪問到的頁面。
在最新的內核版本中,內存管理器將可用的內存公平的分發給兩個隊列,儘可能在保護頻繁訪問的頁面和探測最近使用的頁面之間達到一個折衷的平衡。換言之,內核爲頻率隊列保留了50%的可用內存。
在以前的例子裏,兩個列表一開始都是空的。當第一天的數據被引用的時候,會先進入臨近隊列。在第二次被引用的時候,被提高到了頻率隊列。
接下來,當用戶想使用次日的數據進行工做時,數據文件大於可用內存的50%,可是臨近隊列的空閒空間卻沒那麼大。所以,對這個文件的順序掃描就致使了內存的置換震盪。 第二個文件中的第一個文件系統的頁面會先進入臨近隊列,可是一旦臨近隊列空間被佔滿了之後,這個頁就被從隊列中置換出來了。所以,第二個文件中沒有兩個頁面會在臨近隊列中停留足夠長的時間,由於他們的引用數一直在遞增。
幸運的是,這個問題只有在當你知足以上咱們列出的三點要素時纔會發生。當咱們在這裏討論的時候,問題正在被修復中。若是感興趣的話,你能夠在Linux郵件列表下閱讀更多關於原始問題報告以及提議的一些修復辦法。
對於咱們來講,真正利索的是很容易就定位到了問題所在。由於Citus繼承自PostgreSQL,一旦咱們發現了這個問題,就能夠很快的在Postgres上覆現,以後咱們向linux郵件組提交了咱們的發現,今後社區開始接手。
想發表評論?請加入hacker news的討論。
原文連接:
Metin Doslu
翻譯:
伯樂在線
-
高磊
譯文連接:
http://blog.jobbole.com/52898/
[
轉載必須在正文中標註並保留原文連接、譯文連接和譯者等信息。
]