使用大數據時,別忘了關注Linux內存管理器

原文:http://blog.jobbole.com/52898/

本文由伯樂在線-高磊翻譯自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/
[轉載必須在正文中標註並保留原文鏈接、譯文鏈接和譯者等信息。]


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章