Redis系列:詳解Memcached、Redis等緩存的特征、原理、應(yīng)用
隨著互聯(lián)網(wǎng)的普及,內(nèi)容信息越來(lái)越復(fù)雜,用戶數(shù)和訪問(wèn)量越來(lái)越大,我們的應(yīng)用需要支撐更多的并發(fā)量,同時(shí)我們的應(yīng)用服務(wù)器和數(shù)據(jù)庫(kù)服務(wù)器所做的計(jì)算也越來(lái)越多。但是往往我們的應(yīng)用服務(wù)器資源是有限的,且技術(shù)變革是緩慢的,數(shù)據(jù)庫(kù)每秒能接受的請(qǐng)求次數(shù)也是有限的(或者文件的讀寫(xiě)也是有限的),如何能夠有效利用有限的資源來(lái)提供盡可能大的吞吐量?一個(gè)有效的辦法就是引入緩存,打破標(biāo)準(zhǔn)流程,每個(gè)環(huán)節(jié)中請(qǐng)求可以從緩存中直接獲取目標(biāo)數(shù)據(jù)并返回,從而減少計(jì)算量,有效提升響應(yīng)速度,讓有限的資源服務(wù)更多的用戶。
如圖所示,緩存的使用可以出現(xiàn)在1~4的各個(gè)環(huán)節(jié)中,每個(gè)環(huán)節(jié)的緩存方案與使用各有特點(diǎn)。
上圖: 現(xiàn)在互聯(lián)網(wǎng)應(yīng)用(網(wǎng)站或App)的整體流程
一、緩存特征
緩存也是一個(gè)數(shù)據(jù)模型對(duì)象,那么必然有它的一些特征:
1.命中率
命中率=返回正確結(jié)果數(shù)/請(qǐng)求緩存次數(shù),命中率問(wèn)題是緩存中的一個(gè)非常重要的問(wèn)題,它是衡量緩存有效性的重要指標(biāo)。命中率越高,表明緩存的使用率越高。
2.最大元素(或最大空間)
緩存中可以存放的最大元素的數(shù)量,一旦緩存中元素?cái)?shù)量超過(guò)這個(gè)值(或者緩存數(shù)據(jù)所占空間超過(guò)其最大支持空間),那么將會(huì)觸發(fā)緩存啟動(dòng)清空策略根據(jù)不同的場(chǎng)景合理的設(shè)置最大元素值往往可以一定程度上提高緩存的命中率,從而更有效的時(shí)候緩存。
3.清空策略
如上描述,緩存的存儲(chǔ)空間有限制,當(dāng)緩存空間被用滿時(shí),如何保證在穩(wěn)定服務(wù)的同時(shí)有效提升命中率?這就由緩存清空策略來(lái)處理,設(shè)計(jì)適合自身數(shù)據(jù)特征的清空策略能有效提升命中率。常見(jiàn)的一般策略有:
- FIFO(first in first out)先進(jìn)先出策略,最先進(jìn)入緩存的數(shù)據(jù)在緩存空間不夠的情況下(超出最大元素限制)會(huì)被優(yōu)先被清除掉,以騰出新的空間接受新的數(shù)據(jù)。策略算法主要比較緩存元素的創(chuàng)建時(shí)間。在數(shù)據(jù)實(shí)效性要求場(chǎng)景下可選擇該類(lèi)策略,優(yōu)先保障最新數(shù)據(jù)可用。
- LFU(less frequently used)最少使用策略,無(wú)論是否過(guò)期,根據(jù)元素的被使用次數(shù)判斷,清除使用次數(shù)較少的元素釋放空間。策略算法主要比較元素的hitCount(命中次數(shù))。在保證高頻數(shù)據(jù)有效性場(chǎng)景下,可選擇這類(lèi)策略。
- LRU(least recently used)最近最少使用策略,無(wú)論是否過(guò)期,根據(jù)元素最后一次被使用的時(shí)間戳,清除最遠(yuǎn)使用時(shí)間戳的元素釋放空間。策略算法主要比較元素最近一次被get使用時(shí)間。在熱點(diǎn)數(shù)據(jù)場(chǎng)景下較適用,優(yōu)先保證熱點(diǎn)數(shù)據(jù)的有效性。
除此之外,還有一些簡(jiǎn)單策略比如:
- 根據(jù)過(guò)期時(shí)間判斷,清理過(guò)期時(shí)間最長(zhǎng)的元素;
- 根據(jù)過(guò)期時(shí)間判斷,清理最近要過(guò)期的元素;
- 隨機(jī)清理;
- 根據(jù)關(guān)鍵字(或元素內(nèi)容)長(zhǎng)短清理等。
二、緩存介質(zhì)
雖然從硬件介質(zhì)上來(lái)看,無(wú)非就是內(nèi)存和硬盤(pán)兩種,但從技術(shù)上,可以分成內(nèi)存、硬盤(pán)文件、數(shù)據(jù)庫(kù)。
- 內(nèi)存:將緩存存儲(chǔ)于內(nèi)存中是最快的選擇,無(wú)需額外的I/O開(kāi)銷(xiāo),但是內(nèi)存的缺點(diǎn)是沒(méi)有持久化落地物理磁盤(pán),一旦應(yīng)用異常break down而重新啟動(dòng),數(shù)據(jù)很難或者無(wú)法復(fù)原。
- 硬盤(pán):一般來(lái)說(shuō),很多緩存框架會(huì)結(jié)合使用內(nèi)存和硬盤(pán),在內(nèi)存分配空間滿了或是在異常的情況下,可以被動(dòng)或主動(dòng)的將內(nèi)存空間數(shù)據(jù)持久化到硬盤(pán)中,達(dá)到釋放空間或備份數(shù)據(jù)的目的。
- 數(shù)據(jù)庫(kù):前面有提到,增加緩存的策略的目的之一就是為了減少數(shù)據(jù)庫(kù)的I/O壓力?,F(xiàn)在使用數(shù)據(jù)庫(kù)做緩存介質(zhì)是不是又回到了老問(wèn)題上了?其實(shí),數(shù)據(jù)庫(kù)也有很多種類(lèi)型,像那些不支持SQL,只是簡(jiǎn)單的key-value存儲(chǔ)結(jié)構(gòu)的特殊數(shù)據(jù)庫(kù)(如BerkeleyDB和Redis),響應(yīng)速度和吞吐量都遠(yuǎn)遠(yuǎn)高于我們常用的關(guān)系型數(shù)據(jù)庫(kù)等。
三、緩存分類(lèi)和應(yīng)用場(chǎng)景
緩存有各類(lèi)特征,而且有不同介質(zhì)的區(qū)別,那么實(shí)際工程中我們?cè)趺慈?duì)緩存分類(lèi)呢?在目前的應(yīng)用服務(wù)框架中,比較常見(jiàn)的,時(shí)根據(jù)緩存雨應(yīng)用的藕合度,分為local cache(本地緩存)和remote cache(分布式緩存):
- 本地緩存:指的是在應(yīng)用中的緩存組件,其最大的優(yōu)點(diǎn)是應(yīng)用和cache是在同一個(gè)進(jìn)程內(nèi)部,請(qǐng)求緩存非??焖?,沒(méi)有過(guò)多的網(wǎng)絡(luò)開(kāi)銷(xiāo)等,在單應(yīng)用不需要集群支持或者集群情況下各節(jié)點(diǎn)無(wú)需互相通知的場(chǎng)景下使用本地緩存較合適;同時(shí),它的缺點(diǎn)也是應(yīng)為緩存跟應(yīng)用程序耦合,多個(gè)應(yīng)用程序無(wú)法直接的共享緩存,各應(yīng)用或集群的各節(jié)點(diǎn)都需要維護(hù)自己的單獨(dú)緩存,對(duì)內(nèi)存是一種浪費(fèi)。
- 分布式緩存:指的是與應(yīng)用分離的緩存組件或服務(wù),其最大的優(yōu)點(diǎn)是自身就是一個(gè)獨(dú)立的應(yīng)用,與本地應(yīng)用隔離,多個(gè)應(yīng)用可直接的共享緩存。
目前各種類(lèi)型的緩存都活躍在成千上萬(wàn)的應(yīng)用服務(wù)中,還沒(méi)有一種緩存方案可以解決一切的業(yè)務(wù)場(chǎng)景或數(shù)據(jù)類(lèi)型,我們需要根據(jù)自身的特殊場(chǎng)景和背景,選擇最適合的緩存方案。緩存的使用是程序員、架構(gòu)師的必備技能,好的程序員能根據(jù)數(shù)據(jù)類(lèi)型、業(yè)務(wù)場(chǎng)景來(lái)準(zhǔn)確判斷使用何種類(lèi)型的緩存,如何使用這種緩存,以最小的成本最快的效率達(dá)到最優(yōu)的目的。
- Ehcache
Ehcache是現(xiàn)在最流行的純Java開(kāi)源緩存框架,配置簡(jiǎn)單、結(jié)構(gòu)清晰、功能強(qiáng)大,是一個(gè)非常輕量級(jí)的緩存實(shí)現(xiàn),我們常用的Hibernate里面就集成了相關(guān)緩存功能。
Ehcache框架圖
從圖3中我們可以了解到,Ehcache的核心定義主要包括:
- cache manager:緩存管理器,以前是只允許單例的,不過(guò)現(xiàn)在也可以多實(shí)例了。
- cache:緩存管理器內(nèi)可以放置若干cache,存放數(shù)據(jù)的實(shí)質(zhì),所有cache都實(shí)現(xiàn)了Ehcache接口,這是一個(gè)真正使用的緩存實(shí)例;通過(guò)緩存管理器的模式,可以在單個(gè)應(yīng)用中輕松隔離多個(gè)緩存實(shí)例,獨(dú)立服務(wù)于不同業(yè)務(wù)場(chǎng)景需求,緩存數(shù)據(jù)物理隔離,同時(shí)需要時(shí)又可共享使用。
- element:?jiǎn)螚l緩存數(shù)據(jù)的組成單位。
- system of record(SOR):可以取到真實(shí)數(shù)據(jù)的組件,可以是真正的業(yè)務(wù)邏輯、外部接口調(diào)用、存放真實(shí)數(shù)據(jù)的數(shù)據(jù)庫(kù)等,緩存就是從SOR中讀取或者寫(xiě)入到SOR中去的。
在上層可以看到,整個(gè)Ehcache提供了對(duì)JSR、JMX等的標(biāo)準(zhǔn)支持,能夠較好的兼容和移植,同時(shí)對(duì)各類(lèi)對(duì)象有較完善的監(jiān)控管理機(jī)制。它的緩存介質(zhì)涵蓋堆內(nèi)存(heap)、堆外內(nèi)存(BigMemory商用版本支持)和磁盤(pán),各介質(zhì)可獨(dú)立設(shè)置屬性和策略。Ehcache最初是獨(dú)立的本地緩存框架組件,在后期的發(fā)展中,結(jié)合Terracotta服務(wù)陣列模型,可以支持分布式緩存集群,主要有RMI、JGroups、JMS和Cache Server等傳播方式進(jìn)行節(jié)點(diǎn)間通信,如圖3的左側(cè)部分描述。
Ehcache主要特性:
- 快速,針對(duì)大型高并發(fā)系統(tǒng)場(chǎng)景,Ehcache的多線程機(jī)制有相應(yīng)的優(yōu)化改善。
- 簡(jiǎn)單,很小的jar包,簡(jiǎn)單配置就可直接使用,單機(jī)場(chǎng)景下無(wú)需過(guò)多的其他服務(wù)依賴。
- 支持多種的緩存策略,靈活。
- 緩存數(shù)據(jù)有兩級(jí):內(nèi)存和磁盤(pán),與一般的本地內(nèi)存緩存相比,有了磁盤(pán)的存儲(chǔ)空間,將可以支持更大量的數(shù)據(jù)緩存需求。
- 具有緩存和緩存管理器的偵聽(tīng)接口,能更簡(jiǎn)單方便的進(jìn)行緩存實(shí)例的監(jiān)控管理。
- 支持多緩存管理器實(shí)例,以及一個(gè)實(shí)例的多個(gè)緩存區(qū)域。
注意:Ehcache的超時(shí)設(shè)置主要是針對(duì)整個(gè)cache實(shí)例設(shè)置整體的超時(shí)策略,而沒(méi)有較好的處理針對(duì)單獨(dú)的key的個(gè)性的超時(shí)設(shè)置(有策略設(shè)置,但是比較復(fù)雜,就不描述了),因此,在使用中要注意過(guò)期失效的緩存元素?zé)o法被GC回收,時(shí)間越長(zhǎng)緩存越多,內(nèi)存占用也就越大,內(nèi)存泄露的概率也越大。
- Guava Cache
Guava Cache是Google開(kāi)源的Java重用工具集庫(kù)Guava里的一款緩存工具,其主要實(shí)現(xiàn)的緩存功能有:
- 自動(dòng)將entry節(jié)點(diǎn)加載進(jìn)緩存結(jié)構(gòu)中;
- 當(dāng)緩存的數(shù)據(jù)超過(guò)設(shè)置的最大值時(shí),使用LRU算法移除;
- 具備根據(jù)entry節(jié)點(diǎn)上次被訪問(wèn)或者寫(xiě)入時(shí)間計(jì)算它的過(guò)期機(jī)制;
- 緩存的key被封裝在WeakReference引用內(nèi);
- 緩存的Value被封裝在WeakReference或SoftReference引用內(nèi);
- 統(tǒng)計(jì)緩存使用過(guò)程中命中率、異常率、未命中率等統(tǒng)計(jì)數(shù)據(jù)。
Guava Cache的架構(gòu)設(shè)計(jì)靈感來(lái)源于ConcurrentHashMap,我們前面也提到過(guò),簡(jiǎn)單場(chǎng)景下可以自行編碼通過(guò)hashmap來(lái)做少量數(shù)據(jù)的緩存,但是,如果結(jié)果可能隨時(shí)間改變或者是希望存儲(chǔ)的數(shù)據(jù)空間可控的話,自己實(shí)現(xiàn)這種數(shù)據(jù)結(jié)構(gòu)還是有必要的。
Guava Cache繼承了ConcurrentHashMap的思路,使用多個(gè)segments方式的細(xì)粒度鎖,在保證線程安全的同時(shí),支持高并發(fā)場(chǎng)景需求。Cache類(lèi)似于Map,它是存儲(chǔ)鍵值對(duì)的集合,不同的是它還需要處理evict、expire、dynamic load等算法邏輯,需要一些額外信息來(lái)實(shí)現(xiàn)這些操作。對(duì)此,根據(jù)面向?qū)ο笏枷?,需要做方法與數(shù)據(jù)的關(guān)聯(lián)封裝。如圖5所示cache的內(nèi)存數(shù)據(jù)模型,可以看到,使用ReferenceEntry接口來(lái)封裝一個(gè)鍵值對(duì),而用ValueReference來(lái)封裝Value值,之所以用Reference命令,是因?yàn)镃ache要支持WeakReference Key和SoftReference、WeakReference value。
圖5 Guava Cache數(shù)據(jù)結(jié)構(gòu)圖
總體來(lái)看,Guava Cache基于ConcurrentHashMap的優(yōu)秀設(shè)計(jì)借鑒,在高并發(fā)場(chǎng)景支持和線程安全上都有相應(yīng)的改進(jìn)策略,使用Reference引用命令,提升高并發(fā)下的數(shù)據(jù)……訪問(wèn)速度并保持了GC的可回收,有效節(jié)省空間;同時(shí),write鏈和access鏈的設(shè)計(jì),能更靈活、高效的實(shí)現(xiàn)多種類(lèi)型的緩存清理策略,包括基于容量的清理、基于時(shí)間的清理、基于引用的清理等;編程式的build生成器管理,讓使用者有更多的自由度,能夠根據(jù)不同場(chǎng)景設(shè)置合適的模式。
四、分布式緩存:memcached緩存
memcached是應(yīng)用較廣的開(kāi)源分布式緩存產(chǎn)品之一,它本身其實(shí)不提供分布式解決方案。在服務(wù)端,memcached集群環(huán)境實(shí)際就是一個(gè)個(gè)memcached服務(wù)器的堆積,環(huán)境搭建較為簡(jiǎn)單;cache的分布式主要是在客戶端實(shí)現(xiàn),通過(guò)客戶端的路由處理來(lái)達(dá)到分布式解決方案的目的??蛻舳俗雎酚傻脑矸浅:?jiǎn)單,應(yīng)用服務(wù)器在每次存取某key的value時(shí),通過(guò)某種算法把key映射到某臺(tái)memcached服務(wù)器nodeA上,因此這個(gè)key所有操作都在nodeA上,結(jié)構(gòu)圖如圖6、圖7所示。
圖6 memcached客戶端路由圖
圖7 memcached一致性hash示例圖
memcached客戶端采用一致性hash算法作為路由策略,如圖7,相對(duì)于一般hash(如簡(jiǎn)單取模)的算法,一致性hash算法除了計(jì)算key的hash值外,還會(huì)計(jì)算每個(gè)server對(duì)應(yīng)的hash值,然后將這些hash值映射到一個(gè)有限的值域上(比如0~2^32)。通過(guò)尋找hash值大于hash(key)的最小server作為存儲(chǔ)該key數(shù)據(jù)的目標(biāo)server。如果找不到,則直接把具有最小hash值的server作為目標(biāo)server。同時(shí),一定程度上,解決了擴(kuò)容問(wèn)題,增加或刪除單個(gè)節(jié)點(diǎn),對(duì)于整個(gè)集群來(lái)說(shuō),不會(huì)有大的影響。最近版本,增加了虛擬節(jié)點(diǎn)的設(shè)計(jì),進(jìn)一步提升了可用性。
memcached是一個(gè)高效的分布式內(nèi)存cache,了解memcached的內(nèi)存管理機(jī)制,才能更好的掌握memcached,讓我們可以針對(duì)我們數(shù)據(jù)特點(diǎn)進(jìn)行調(diào)優(yōu),讓其更好的為我所用。我們知道m(xù)emcached僅支持基礎(chǔ)的key-value鍵值對(duì)類(lèi)型數(shù)據(jù)存儲(chǔ)。在memcached內(nèi)存結(jié)構(gòu)中有兩個(gè)非常重要的概念:slab和chunk。如圖8所示。
圖8 memcached內(nèi)存結(jié)構(gòu)圖
總結(jié)來(lái)看,memcached內(nèi)存管理需要注意的幾個(gè)方面:
- chunk是在page里面劃分的,而page固定為1m,所以chunk最大不能超過(guò)1m。
- chunk實(shí)際占用內(nèi)存要加48B,因?yàn)閏hunk數(shù)據(jù)結(jié)構(gòu)本身需要占用48B。
- 如果用戶數(shù)據(jù)大于1m,則memcached會(huì)將其切割,放到多個(gè)chunk內(nèi)。
- 已分配出去的page不能回收。
對(duì)于key-value信息,最好不要超過(guò)1m的大?。煌瑫r(shí)信息長(zhǎng)度最好相對(duì)是比較均衡穩(wěn)定的,這樣能夠保障最大限度的使用內(nèi)存;同時(shí),memcached采用的LRU清理策略,合理甚至過(guò)期時(shí)間,提高命中率。
無(wú)特殊場(chǎng)景下,key-value能滿足需求的前提下,使用memcached分布式集群是較好的選擇,搭建與操作使用都比較簡(jiǎn)單;分布式集群在單點(diǎn)故障時(shí),只影響小部分?jǐn)?shù)據(jù)異常,目前還可以通過(guò)Magent緩存代理模式,做單點(diǎn)備份,提升高可用;整個(gè)緩存都是基于內(nèi)存的,因此響應(yīng)時(shí)間是很快,不需要額外的序列化、反序列化的程序,但同時(shí)由于基于內(nèi)存,數(shù)據(jù)沒(méi)有持久化,集群故障重啟數(shù)據(jù)無(wú)法恢復(fù)。高版本的memcached已經(jīng)支持CAS模式的原子操作,可以低成本的解決并發(fā)控制問(wèn)題。
五、Redis緩存
Redis是一個(gè)遠(yuǎn)程內(nèi)存數(shù)據(jù)庫(kù)(非關(guān)系型數(shù)據(jù)庫(kù)),性能強(qiáng)勁,具有復(fù)制特性以及解決問(wèn)題而生的獨(dú)一無(wú)二的數(shù)據(jù)模型。它可以存儲(chǔ)鍵值對(duì)與5種不同類(lèi)型的值之間的映射,可以將存儲(chǔ)在內(nèi)存的鍵值對(duì)數(shù)據(jù)持久化到硬盤(pán),可以使用復(fù)制特性來(lái)擴(kuò)展讀性能,還可以使用客戶端分片來(lái)擴(kuò)展寫(xiě)性能。
圖9 Redis數(shù)據(jù)模型圖
如圖9,Redis內(nèi)部使用一個(gè)redisObject對(duì)象來(lái)標(biāo)識(shí)所有的key和value數(shù)據(jù),redisObject最主要的信息如圖所示:type代表一個(gè)value對(duì)象具體是何種數(shù)據(jù)類(lèi)型,encoding是不同數(shù)據(jù)類(lèi)型在Redis內(nèi)部的存儲(chǔ)方式,比如——type=string代表value存儲(chǔ)的是一個(gè)普通字符串,那么對(duì)應(yīng)的encoding可以是raw或是int,如果是int則代表世界Redis內(nèi)部是按數(shù)值類(lèi)型存儲(chǔ)和表示這個(gè)字符串。
從網(wǎng)絡(luò)I/O模型上看,Redis使用單線程的I/O復(fù)用模型,自己封裝了一個(gè)簡(jiǎn)單的AeEvent事件處理框架,主要實(shí)現(xiàn)了epoll、kqueue和select。對(duì)于單純只有I/O操作來(lái)說(shuō),單線程可以將速度優(yōu)勢(shì)發(fā)揮到最大,但是Redis也提供了一些簡(jiǎn)單的計(jì)算功能,比如排序、聚合等,對(duì)于這些操作,單線程模型實(shí)際會(huì)嚴(yán)重影響整體吞吐量,CPU計(jì)算過(guò)程中,整個(gè)I/O調(diào)度都是被阻塞住的,在這些特殊場(chǎng)景的使用中,需要額外的考慮。
相較于memcached的預(yù)分配內(nèi)存管理,Redis使用現(xiàn)場(chǎng)申請(qǐng)內(nèi)存的方式來(lái)存儲(chǔ)數(shù)據(jù),并且很少使用free-list等方式來(lái)優(yōu)化內(nèi)存分配,會(huì)在一定程度上存在內(nèi)存碎片。Redis跟據(jù)存儲(chǔ)命令參數(shù),會(huì)把帶過(guò)期時(shí)間的數(shù)據(jù)單獨(dú)存放在一起,并把它們稱為臨時(shí)數(shù)據(jù),非臨時(shí)數(shù)據(jù)是永遠(yuǎn)不會(huì)被剔除的,即便物理內(nèi)存不夠,導(dǎo)致swap也不會(huì)剔除任何非臨時(shí)數(shù)據(jù)(但會(huì)嘗試剔除部分臨時(shí)數(shù)據(jù))。
我們描述Redis為內(nèi)存數(shù)據(jù)庫(kù),作為緩存服務(wù),大量使用內(nèi)存間的數(shù)據(jù)快速讀寫(xiě),支持高并發(fā)大吞吐;而作為數(shù)據(jù)庫(kù),則是指Redis對(duì)緩存的持久化支持。Redis由于支持了非常豐富的內(nèi)存數(shù)據(jù)庫(kù)結(jié)構(gòu)類(lèi)型,如何把這些復(fù)雜的內(nèi)存組織方式持久化到磁盤(pán)上?Redis的持久化與傳統(tǒng)數(shù)據(jù)庫(kù)的方式差異較大,Redis一共支持四種持久化方式,主要使用的兩種:
- 定時(shí)快照方式(snapshot):該持久化方式實(shí)際是在Redis內(nèi)部一個(gè)定時(shí)器事件,每隔固定時(shí)間去檢查當(dāng)前數(shù)據(jù)發(fā)生的改變次數(shù)與時(shí)間是否滿足配置的持久化觸發(fā)的條件,如果滿足則通過(guò)操作系統(tǒng)fork調(diào)用來(lái)創(chuàng)建出一個(gè)子進(jìn)程,這個(gè)子進(jìn)程默認(rèn)會(huì)與父進(jìn)程共享相同的地址空間,這時(shí)就可以通過(guò)子進(jìn)程來(lái)遍歷整個(gè)內(nèi)存來(lái)進(jìn)行存儲(chǔ)操作,而主進(jìn)程則仍然可以提供服務(wù),當(dāng)有寫(xiě)入時(shí)由操作系統(tǒng)按照內(nèi)存頁(yè)(page)為單位來(lái)進(jìn)行copy-on-write保證父子進(jìn)程之間不會(huì)互相影響。它的缺點(diǎn)是快照只是代表一段時(shí)間內(nèi)的內(nèi)存映像,所以系統(tǒng)重啟會(huì)丟失上次快照與重啟之間所有的數(shù)據(jù)。
- 基于語(yǔ)句追加文件的方式(aof):aof方式實(shí)際類(lèi)似MySQl的基于語(yǔ)句的binlog方式,即每條會(huì)使Redis內(nèi)存數(shù)據(jù)發(fā)生改變的命令都會(huì)追加到一個(gè)log文件中,也就是說(shuō)這個(gè)log文件就是Redis的持久化數(shù)據(jù)。
aof的方式的主要缺點(diǎn)是追加log文件可能導(dǎo)致體積過(guò)大,當(dāng)系統(tǒng)重啟恢復(fù)數(shù)據(jù)時(shí)如果是aof的方式則加載數(shù)據(jù)會(huì)非常慢,幾十G的數(shù)據(jù)可能需要幾小時(shí)才能加載完,當(dāng)然這個(gè)耗時(shí)并不是因?yàn)榇疟P(pán)文件讀取速度慢,而是由于讀取的所有命令都要在內(nèi)存中執(zhí)行一遍。另外由于每條命令都要寫(xiě)log,所以使用aof的方式,Redis的讀寫(xiě)性能也會(huì)有所下降。
Redis的持久化使用了Buffer I/O,所謂Buffer I/O是指Redis對(duì)持久化文件的寫(xiě)入和讀取操作都會(huì)使用物理內(nèi)存的Page Cache,而大多數(shù)數(shù)據(jù)庫(kù)系統(tǒng)會(huì)使用Direct I/O來(lái)繞過(guò)這層Page Cache并自行維護(hù)一個(gè)數(shù)據(jù)的Cache。而當(dāng)Redis的持久化文件過(guò)大(尤其是快照文件),并對(duì)其進(jìn)行讀寫(xiě)時(shí),磁盤(pán)文件中的數(shù)據(jù)都會(huì)被加載到物理內(nèi)存中作為操作系統(tǒng)對(duì)該文件的一層Cache,而這層Cache的數(shù)據(jù)與Redis內(nèi)存中管理的數(shù)據(jù)實(shí)際是重復(fù)存儲(chǔ)的。雖然內(nèi)核在物理內(nèi)存緊張時(shí)會(huì)做Page Cache的剔除工作,但內(nèi)核很可能認(rèn)為某塊Page Cache更重要,而讓你的進(jìn)程開(kāi)始Swap,這時(shí)你的系統(tǒng)就會(huì)開(kāi)始出現(xiàn)不穩(wěn)定或者崩潰了,因此在持久化配置后,針對(duì)內(nèi)存使用需要實(shí)時(shí)監(jiān)控觀察。
與memcached客戶端支持分布式方案不同,Redis更傾向于在服務(wù)端構(gòu)建分布式存儲(chǔ),如圖Redis分布式集群圖: