天天看點

九.redis學習筆記之虛拟記憶體

  首先說明下redis的虛拟記憶體與os的虛拟記憶體不是一碼事,但是思路和目的都是相同的。就是暫時把不經常通路的資料從記憶體交換到磁盤中,進而騰出寶貴的 記憶體空間用于其他需要通路的資料。尤其是對于redis這樣的記憶體資料庫,記憶體總是不夠用的。除了可以将資料分割到多個redis server外。另外的能夠提高資料庫容量的辦法就是使用vm把那些不經常通路的資料交換的磁盤上。如果我們的存儲的資料總是有少部分資料被經常通路,大 部分資料很少被通路,對于網站來說确實總是隻有少量使用者經常活躍。當少量資料被經常通路時,使用vm不但能提高單台redis

server資料庫的容量,而且也不會對性能造成太多影響。

主要的理由有兩點

1.os 的虛拟記憶體是已4k頁面為最小機關進行交換的。而redis的大多數對象都遠小于4k,是以一個os頁面上可能有多個redis對象。另外redis的集 合對象類型如list,set可能存在與多個os頁面上。最終可能造成隻有10%key被經常通路,但是所有os頁面都會被os認為是活躍的,這樣隻有内 存真正耗盡時os才會交換頁面。

2.相比于os的交換方式。redis可以将被交換到磁盤的對象進行壓縮,儲存到磁盤的對象可以去除指針和對象中繼資料資訊。一般壓縮後的對象會比記憶體中的對象小10倍。這樣redis的vm會比os vm能少做很多io操作。

下面是vm相關配置

vm-enabled yes                          #開啟vm功能

vm-swap-file /tmp/redis.swap                 #交換出來的value儲存的檔案路徑/tmp/redis.swap

vm-max-memory 1000000                    #redis使用的最大記憶體上限,超過上限後redis開始交換value到磁盤檔案中。

vm-page-size 32                    #每個頁面的大小32個位元組

vm-pages 134217728                 #最多使用在檔案中使用多少頁面,交換檔案的大小 = vm-page-size * vm-pages

vm-max-threads 4                    #用于執行value對象換入換出的工作線程數量。0表示不使用工作線程(後面介紹)

       redis的vm在設計上為了保證key的查找速度,隻會将value交換到swap檔案中。是以如果是記憶體問題是由于太多value很小的key造成 的,那麼vm并不能解決。和os一樣redis也是按頁面來交換對象的。redis規定同一個頁面隻能儲存一個對象。但是一個對象可以儲存在多個頁面中。 在redis使用的記憶體沒超過vm-max-memory之前是不會交換任何value的。當超過最大記憶體限制後,redis會選擇較老的對象。如果兩個 對象一樣老會優先交換比較大的對象,精确的公式swappability

= age*log(size_in_memory)。 對于vm-page-size的設定應該根據自己的應用将頁面的大小設定為可以容納大多數對象的大小。太大了會浪費磁盤空間,太小了會造成交換檔案出現碎 片。對于交換檔案中的每個頁面,redis會在記憶體中對應一個1bit值來記錄頁面的空閑狀态。是以像上面配置中頁面數量(vm-pages 134217728 )會占用16M記憶體用來記錄頁面空閑狀态。vm-max-threads表示用做交換任務的線程數量。如果大于0推薦設為伺服器的cpu core的數量。如果是0則交換過程在主線程進行。

參數配置讨論完後,在來簡單介紹下vm是如何工作的,

當vm-max-threads設為0時(Blocking VM)

換出

主線程定期檢查發現記憶體超出最大上限後,會直接已阻塞的方式,将選中的對象儲存到swap檔案中,并釋放對象占用的記憶體,此過程會一直重複直到下面條件滿足

1.記憶體使用降到最大限制以下

2.swap檔案滿了

3.幾乎全部的對象都被交換到磁盤了

換入

當有client請求value被換出的key時。主線程會以阻塞的方式從檔案中加載對應的value對象,加載時此時會阻塞是以client。然後處理client的請求

當vm-max-threads大于0(Threaded VM)

當主線程檢測到使用記憶體超過最大上限,會将選中的要交換的對象資訊放到一個隊列中交由工作線程背景處理,主線程會繼續處理client請求。

如果有client請求的key被換出了,主線程先阻塞發出指令的client,然後将加載對象的資訊放到一個隊列中,讓工作線程去加載。加載完畢後工作線程通知主線程。主線程再執行client的指令。這種方式隻阻塞請求value被換出key的client

總 的來說blocking vm的方式總的性能會好一些,因為不需要線程同步,建立線程和恢複被阻塞的client等開銷。但是也相應的犧牲了響應性。threaded vm的方式主線程不會阻塞在磁盤io上,是以響應性更好。如果我們的應用不太經常發生換入換出,而且也不太在意有點延遲的話則推薦使用blocking vm的方式。關于redis vm的更詳細介紹可以參考下面連結

<a href="http://antirez.com/post/redis-virtual-memory-story.html">http://antirez.com/post/redis-virtual-memory-story.html</a>

<a href="http://redis.io/topics/internals-vm">http://redis.io/topics/internals-vm</a>

redcreen