盡管Redis的虛擬內(nèi)存是首次出現(xiàn)在Redis 2.0穩(wěn)定發(fā)布版的特性之一。但是通過對Git上發(fā)布的Redis分支所做的大量測試證明,虛擬內(nèi)存技術(shù)目前已經(jīng)是穩(wěn)定可靠的。
Redis遵循一種Key-Value鍵值模型。你可以將鍵與一些值關(guān)聯(lián)起來。通常情況下,Redis把鍵和相關(guān)聯(lián)的值都存儲在內(nèi)存中,但有時候這未必是最佳選擇。為了確保能夠快速查找,鍵必須存儲在內(nèi)存中,但是可以把一些很少使用的值交換至磁盤中。
在實(shí)踐過程中,如果你的內(nèi)存中存有10w個鍵的數(shù)據(jù)集,但是只有10%的鍵是經(jīng)常使用的,那么開啟了虛擬內(nèi)存機(jī)制的Redis就會把這些利用率不高的鍵所對應(yīng)的值數(shù)據(jù)交換至磁盤中。
當(dāng)客戶端發(fā)起一條命令,請求訪問這些冷門數(shù)據(jù)并作為結(jié)果返回的時候,這些值又會從交換文件中重新加載至內(nèi)存中。
在你決定使用虛擬內(nèi)存之前,你應(yīng)該問自己是否真的需要這個特性。Redis只是一種用磁盤做備份的內(nèi)存數(shù)據(jù)庫。Redis的使用之道應(yīng)該是盡可能的使用足夠的內(nèi)存以保證數(shù)據(jù)全部存放在內(nèi)存中。但是下列場景中不是必須如此:
有一點(diǎn)非常值得注意,Redis無法將鍵交換至磁盤中,因此如果你的內(nèi)存問題是因太多值很小的鍵引起,虛擬內(nèi)存機(jī)制是幫不了你的。
但是,如果大部分內(nèi)存開銷都用于大容量的值存儲時(例如很大的字符串,列表,集合或者很多元素的哈希),那么選用虛擬內(nèi)存機(jī)制是一個好主意。
有時候,你可以通過采用Hash結(jié)構(gòu)將相關(guān)數(shù)據(jù)分組存入到一個單鍵所對應(yīng)的字段,把“大量值很小的鍵”情況所引發(fā)的問題,轉(zhuǎn)化成“鍵很少但是值很大”的情況。舉個例子,通過把一個對象的不同屬性映射至一個單鍵的Hash字段上,來取代為對象的每個屬性都使用一個鍵的方案。(思門注:Hash數(shù)據(jù)結(jié)構(gòu)同樣從Redis2.x版本開始出現(xiàn))
配置Redis的虛擬內(nèi)存并不難,但是應(yīng)該根據(jù)需求細(xì)心的設(shè)置最佳的參數(shù)。
虛擬內(nèi)存的開啟和配置都是通過編輯redis.conf文件實(shí)現(xiàn),首先將它啟用:
vm-enabled yes
有大量的選項(xiàng)可以改變虛擬內(nèi)存的性能。如果不希望使用默認(rèn)配置運(yùn)行每個數(shù)據(jù)集,那么你需要做一些細(xì)微的調(diào)整以獲取最大的性能。
vm-max-memory選項(xiàng)用于指定當(dāng)Redis占用多大內(nèi)存空間后,才將數(shù)據(jù)交換至磁盤。
基本上如果(內(nèi)存使用)沒有達(dá)到這個內(nèi)存限制,是不會有任何對象被交換至磁盤的,Redis將像往常一樣在內(nèi)存中使用所有對象。如果一旦達(dá)到該限制,那么Redis將會盡可能的把數(shù)據(jù)交換出去,以保證內(nèi)存使用回到限制范圍之內(nèi)。
被交換出去的對象主要是”老齡化“(也就是,那些已經(jīng)有很長時間沒有被使用過的)的這些數(shù)據(jù),并且一個對象的”被交換能力“與它在內(nèi)存中所占用的空間大小是成對數(shù)比例的。所以,舊對象是優(yōu)先考慮的,當(dāng)生存期差不多的時候,值內(nèi)容更大的對象將被優(yōu)先交換出去。
警告:由于在Redis中鍵是無法被交換出去的,因此如果鍵占用的空間超過了內(nèi)存限制,Redis的vm-max-memory選項(xiàng)是無法生效的。
這個選項(xiàng)最大的價值就是將“工作集”中的數(shù)據(jù)存儲于足夠的內(nèi)存中。在實(shí)踐過程中,盡可能給Redis分配更多的內(nèi)存,交換工作將會做的更好。
為了把數(shù)據(jù)從內(nèi)存上轉(zhuǎn)移至磁盤中,Redis使用了交換文件。交換文件對數(shù)據(jù)持久化沒有太大的作用,并且可以在Redis進(jìn)程終止后刪除。但是,在Redis運(yùn)行過程中,不應(yīng)對交換文件進(jìn)行移動,刪除或者修改操作。
因?yàn)镽edis的交換文件大多使用隨機(jī)訪問方式,所以把交換文件存放于固態(tài)磁盤中會得到更好的性能。
交換文件被分成多個“頁”。一個值可以被交換至一個或更多頁中,但是一個單頁內(nèi)不能保存多于一個的值。
目前還沒有直接的方法,告訴Redis的交換文件應(yīng)該使用多少字節(jié)大小。而是通過配置兩個不同的參數(shù),將它們的乘積用于計算可用的字節(jié)總數(shù)。這兩個參數(shù)分別是交換文件中的頁數(shù),和單頁的大小??梢栽趓edis.conf配置文件中配置這兩個參數(shù)。
因此,如果每頁大小設(shè)置為32字節(jié),并且總頁數(shù)設(shè)置為1000w頁,那么交換文件總共可以存儲320MB的數(shù)據(jù)。
因?yàn)槊宽摱疾荒鼙4娉^限制大小的內(nèi)容(但是一個值可以被存儲于多個頁中),因此在設(shè)置這些參數(shù)的時候得格外小心。通常而言最好的方式是調(diào)節(jié)頁大小,讓大多數(shù)值被交換至盡可能少的頁中。
另一個非常重要的配置參數(shù)就是vm-max-threads:
# The default vm-max-threads configuration
vm-max-threads 4
這是交換文件執(zhí)行I/O操作時所能用到的最大線程數(shù)。合適的參數(shù)值是與你的系統(tǒng)內(nèi)核數(shù)保持一致。
然而特殊值“0”將會啟用阻塞式虛擬內(nèi)存。當(dāng)虛擬內(nèi)存配置為阻塞式后,它將以同步阻塞方式執(zhí)行I/O操作。這些就是你可以從阻塞式虛擬內(nèi)存中預(yù)期得到的:
相反,如果你有大量的數(shù)據(jù)交換出入的操作,想充分利用多核特性,并且不希望客戶端在處理數(shù)據(jù)交換的時候被其他客戶端中斷幾毫秒(或者更多需要交換的數(shù)據(jù)都比較大)時,最好還是使用線程虛擬內(nèi)存。
熱情期望您使用不同的配置和數(shù)據(jù)進(jìn)行體驗(yàn)。
在許多配置中,交換文件尺寸可以設(shè)置成相當(dāng)大,40GB甚至更多。并不是所有的文件系統(tǒng)都可以很好的處理這么大的文件,尤其是在蹩腳的Mac OS X文件系統(tǒng)中。官方推薦使用的是Linux系統(tǒng)下的Ext3文件系統(tǒng),或者是其他能夠良好支持稀疏文件的文件系統(tǒng)。什么是稀疏文件?
稀疏文件就是留有很多空余空間的文件。高級的文件系統(tǒng),像ext2, ext3, ext4, RaiserFS, Raiser4,還有一些其他的,都能夠以一種更有效的方式將文件編碼,并且在需要時分配更多的可用空間,也就是說,在文件中使用更多實(shí)際的塊。
很顯然,交換文件相當(dāng)稀疏,尤其是服務(wù)器剛運(yùn)行了一小段時間,或者文件自身大小相比交換出去的數(shù)據(jù)量而言很大的情形。當(dāng)立即創(chuàng)建一個很大的文件時,一個不支持稀疏文件的文件系統(tǒng)在某種程度上可能會導(dǎo)致Redis進(jìn)程阻塞。
至于支持稀疏文件的文件系統(tǒng)列表,可以將參見維基百科對不同文件系統(tǒng)的對比(via)。
一旦你選擇了開啟Redis系統(tǒng)的虛擬內(nèi)存機(jī)制并運(yùn)行,你一定會很感興趣它是如何工作的:總共有多少對象被交換,每秒交換和載入的對象數(shù)量,等等。
有一個用于檢測虛擬內(nèi)存工作狀態(tài)非常順手的工具,它是Redis Tools的其中一部分。這個工具叫redis-stat,而且用起來相當(dāng)清晰。
以上輸出的是數(shù)據(jù)是關(guān)于一臺開啟了虛擬內(nèi)存機(jī)制,大約有100w鍵在內(nèi),并且通過redis-load工具模擬載入的Redis服務(wù)器。
正如你所見,每秒都會產(chǎn)生大量的load-in和swap-out操作。需要注意的是,第一行結(jié)果記錄的是自服務(wù)器啟動后的實(shí)際值,后面顯示的都是和第一行對比的差異數(shù)據(jù)。
如果你為保持?jǐn)?shù)據(jù)賦予了足夠多的內(nèi)存,很有可能你看到的只有一小部分?jǐn)?shù)據(jù)交換發(fā)生,因此redis-stat是一個相當(dāng)有價值的工具,以便讓你知道是否需要去購買新的內(nèi)存條了:)
當(dāng)虛擬內(nèi)存開啟時,保存和加載數(shù)據(jù)庫的時候會比較慢。如果在服務(wù)器盡可能使用最小內(nèi)存的情況下(舉個例子,vm-max-memory設(shè)置為0),通常情況下只需要2秒就能載入的數(shù)據(jù),在開啟虛擬內(nèi)存的時候需要13秒才能完成。
因此你可能希望為了持久化把配置文件切換至增量文件模式,這樣你可以時不時的使用BGREWRITEAOF命令。
很重要的一點(diǎn),當(dāng)BGSAVE或者BGREWRITEAOF命令在執(zhí)行過程中的時候,Redis是不會把新產(chǎn)生的數(shù)據(jù)交換到磁盤上的。當(dāng)另外一個子進(jìn)程訪問的時候,虛擬內(nèi)存將變?yōu)橹蛔x狀態(tài)。因此當(dāng)子進(jìn)程工作時如果產(chǎn)生大量的數(shù)據(jù)寫入操作,內(nèi)存占用量可能會攀升。
一種有意思的Redis安裝方式:通過將vm-max-memory設(shè)置為0,僅把鍵存在內(nèi)存中來實(shí)現(xiàn)一個基于磁盤的數(shù)據(jù)庫。如果你不介意延遲高一些,性能損失一些,只是希望為內(nèi)容很大的值少花費(fèi)一些內(nèi)存開銷,這也是一個不錯的安裝方法。
在這種安裝模式下,數(shù)據(jù)交換會產(chǎn)生的巨大流量,線程開銷也會消耗大量的資源,你應(yīng)該嘗試開啟阻塞式虛擬內(nèi)存(vm-max-threads設(shè)置為0)。
虛擬內(nèi)存仍然是實(shí)驗(yàn)性代碼,但是最近幾周在測試環(huán)境中已經(jīng)經(jīng)歷了多種測試,甚至在一些生產(chǎn)環(huán)境中也使用了。在這些測試期間,沒有發(fā)現(xiàn)任何Bug。不過在那些我們因?yàn)槟承┰驘o法復(fù)制的環(huán)境中,仍然可能存在某些隱藏的問題。
現(xiàn)階段,我們鼓勵你在開發(fā)環(huán)境以及處理非核心業(yè)務(wù)的生產(chǎn)環(huán)境中嘗試VM機(jī)制,比如只是一個大的數(shù)據(jù)持久化緩存消失時,不會有太多的問題的情況下。
請把你遇到的任何問題,通過Redis谷歌小組或者加入Redis的IRC頻道,報告給我們。