Redis VM使用
Redis處理的速度很快,因?yàn)樗腔趦?nèi)存的。在內(nèi)存能夠足夠容納數(shù)據(jù)的時(shí)候,所有的數(shù)據(jù)都存放在內(nèi)存。這個(gè)時(shí)候不論是讀取數(shù)據(jù)還是寫(xiě)入數(shù)據(jù)都是非??斓?。但是如果數(shù)據(jù)量很大,大到內(nèi)存已經(jīng)無(wú)法全部容納的時(shí)候,我想對(duì)存儲(chǔ)有一定了解的人都在想,這個(gè)時(shí)候redis是怎么處理的呢?處理速度是否會(huì)直線下降?
幸虧,答案是否定的。Redis使用到了VM,在redis.conf設(shè)置vm-enabled yes 即開(kāi)啟VM功能。 通過(guò)VM功能可以實(shí)現(xiàn)冷熱數(shù)據(jù)分離。使熱數(shù)據(jù)仍在內(nèi)存中,冷數(shù)據(jù)保存到磁盤(pán)。這樣就可以避免因?yàn)閮?nèi)存不足而造成訪問(wèn)速度下降的問(wèn)題。在這里,需要特別提到的是,Redis并沒(méi)有使用OS提供的Swap,而是自己實(shí)現(xiàn)。作者在自己的blog說(shuō)明了原因:
1:OS是基于page(4K)來(lái)做的,它的粒度對(duì)于Redis來(lái)說(shuō)太大。而redis的大多數(shù)對(duì)象都遠(yuǎn)小于4k,所以一個(gè)OS頁(yè)面上可能有多個(gè)redis對(duì)象。另外redis的集合對(duì)象類(lèi)型如list,set可能存在與多個(gè)OS頁(yè)面上。最終可能造成只有10%的key被經(jīng)常訪問(wèn),但是所有OS頁(yè)面都會(huì)被OS認(rèn)為是活躍的,這樣只有內(nèi)存真正耗盡時(shí)OS才會(huì)交換頁(yè)面。
2:相比于OS的交換方式。redis可以將被交換到磁盤(pán)的對(duì)象進(jìn)行壓縮,保存到磁盤(pán)的對(duì)象可以去除指針和對(duì)象元數(shù)據(jù)信息。一般壓縮后對(duì)象會(huì)比內(nèi)存中對(duì)象小10倍。這樣redis的vm會(huì)比OS vm能少做很多io操作。
3:OS交換的時(shí)候,是會(huì)阻塞線程的,而Redis可以設(shè)置讓工作線程來(lái)完成,主線程仍可以繼續(xù)接收client的請(qǐng)求。
開(kāi)啟VM功能后,還有其他幾個(gè)設(shè)置需要設(shè)置vm-swap-file:設(shè)置被交換出的值保存到磁盤(pán)的位置。vm-max-memory:設(shè)置當(dāng)內(nèi)存消耗達(dá)到上限時(shí)開(kāi)始將value交換出來(lái)。vm-page-size:設(shè)置單個(gè)頁(yè)面的大小,單位是字節(jié)。vm-pages:設(shè)置最多能交換保存多少個(gè)頁(yè)到磁盤(pán)??梢运愠隹梢越粨Q出來(lái)的最大大小為vm-page-size * vm-pages。vm-max-threads:設(shè)置完成交換動(dòng)作的工作線程數(shù),設(shè)置為0表示不使用工作線程而使用主線程,這會(huì)以阻塞的方式來(lái)運(yùn)行。建議設(shè)置成CPU核個(gè)數(shù)。
Redis為了保證查找的速度,只會(huì)將value交換出去,而在內(nèi)存中保留所有的Key。所以它非常適合Key很小,Value很大的存儲(chǔ)結(jié)構(gòu)。如果Key很大,value很小,那么vm可能還是無(wú)法滿足需求。redis規(guī)定同一個(gè)頁(yè)面只能保存一個(gè)對(duì)象。但是一個(gè)對(duì)象可以保存在多個(gè)頁(yè)面中。在redis使用的內(nèi)存沒(méi)超過(guò)vm-max-memory之前是不會(huì)交換任何value的。當(dāng)超過(guò)最大內(nèi)存限制后,redis會(huì)選擇較老的對(duì)象。如果兩個(gè)對(duì)象一樣老會(huì)優(yōu)先交換比較大的對(duì)象,將它從內(nèi)存中移除,這樣會(huì)更加節(jié)約內(nèi)存。精確的公式swappability = age*log(size_in_memory)。
既然對(duì)于Redis來(lái)說(shuō),一個(gè)頁(yè)面只會(huì)保存一個(gè)對(duì)象,也就是一個(gè)Value值,所以應(yīng)該將vm-page-size設(shè)置成大多數(shù)value可以保存進(jìn)去。如果設(shè)置太小,一個(gè)value對(duì)象就會(huì)占用幾個(gè)頁(yè)面,如果設(shè)置太大,就會(huì)造成頁(yè)面空閑空間浪費(fèi)。每個(gè)頁(yè)面,Redis都會(huì)在內(nèi)存中使用1比特(bit)長(zhǎng)度來(lái)保存頁(yè)面的空閑狀態(tài)。如果設(shè)置的vm-pages非常大,那么光用來(lái)保存頁(yè)面狀態(tài)就會(huì)花費(fèi)很大的內(nèi)存。
VM的工作機(jī)制:分為兩種
第一種:vm-max-threads = 0
換出
主線程定期檢查使用的內(nèi)存大小,如果發(fā)現(xiàn)內(nèi)存超出最大上限,會(huì)直接以阻塞的方式,將選中的對(duì)象保存到換出文件中,并釋放對(duì)象占用的內(nèi)存,此過(guò)程會(huì)一直重復(fù)直到下面條件滿足:
1.內(nèi)存使用降到最大限制以下,2. 設(shè)置的交換文件數(shù)量達(dá)到上限,3. 幾乎全部的對(duì)象都被交換到磁盤(pán)了
換入
當(dāng)有client請(qǐng)求的value之前已被換出時(shí),主線程會(huì)以阻塞的方式從換出文件中加載對(duì)應(yīng)的value對(duì)象,加載時(shí)此時(shí)會(huì)阻塞所有client,然后再處理client的請(qǐng)求。
第二種:vm-max-threads > 0
換出
當(dāng)主線程檢測(cè)到使用內(nèi)存超過(guò)最大上限,會(huì)將選中的要交換的對(duì)象信息放到一個(gè)隊(duì)列中交由工作線程后臺(tái)處理,主線程會(huì)繼續(xù)處理client請(qǐng)求。
換入
如果有client請(qǐng)求value之前已被換出時(shí),主線程先阻塞當(dāng)個(gè)client,然后將加載對(duì)象的信息放到一個(gè)隊(duì)列中,讓工作線程去加載,此進(jìn)主線程繼續(xù)處理其他client請(qǐng)求。加載完畢后工作線程通知主線程。主線程再執(zhí)行被阻塞的client的命令。這種方式只阻塞單個(gè)client。