1.Memcached 簡(jiǎn)介
Memcached 是一個(gè)開(kāi)源的,支持高性能,高并發(fā)的分布式內(nèi)存緩存系統(tǒng),由 C 語(yǔ)言編寫(xiě),總共 2000 多行代碼。從軟件名稱(chēng)上看,前 3 個(gè)字符 Mem
就是內(nèi)存的意思,而接下來(lái)的后面 5 個(gè)字符 cache
就是緩存的意思,最后一個(gè)字符 d
,是 daemon
的意思,代表的是服務(wù)器端守護(hù)進(jìn)程模式服務(wù)。
Memcached 服務(wù)分為服務(wù)器端和客戶(hù)端兩部分。其中,服務(wù)器端軟件的名字形如 Memcached-1.4.24.tar.gz
,客戶(hù)端軟件的名字形如 Memcache-2.25.tar.gz
。
Memcached 軟件誕生于 2003 年,最初由 LiveJournal 的 Brad Fitzpatrick 開(kāi)發(fā)完成。Memcache 是整個(gè)項(xiàng)目的名稱(chēng),而 Memcached 是服務(wù)器端的主程序名,因其協(xié)議簡(jiǎn)單,應(yīng)用部署方便,且支持高并發(fā),因此被互聯(lián)網(wǎng)企業(yè)廣泛使用,直到現(xiàn)在仍然如此。其官方網(wǎng)站地址:http://memcached.org/。
Memcached 的作用
傳統(tǒng)場(chǎng)景中,多數(shù) Web 應(yīng)用都將數(shù)據(jù)保存到關(guān)系型數(shù)據(jù)庫(kù)中(如 MySQL),Web 服務(wù)器從中讀取數(shù)據(jù)并在瀏覽器中顯示。但隨著數(shù)據(jù)量增大、訪問(wèn)集中,關(guān)系型數(shù)據(jù)庫(kù)的負(fù)擔(dān)就會(huì)出現(xiàn)加重,響應(yīng)緩慢,導(dǎo)致網(wǎng)站打開(kāi)延遲等問(wèn)題,影響用戶(hù)體驗(yàn)。
這時(shí)就需要 Memcached 軟件出馬了。使用 Memcached 的主要目的是,通過(guò)在自身內(nèi)存中緩存關(guān)系型數(shù)據(jù)庫(kù)的查詢(xún)結(jié)果,減少數(shù)據(jù)庫(kù)被訪問(wèn)的次數(shù),以提高動(dòng)態(tài) Web 應(yīng)用的速度,提高網(wǎng)站架構(gòu)的并發(fā)能力和可擴(kuò)展性。
Memcached 服務(wù)的運(yùn)行原理是通過(guò)在事先規(guī)劃好的系統(tǒng)內(nèi)存空間中臨時(shí)緩存數(shù)據(jù)庫(kù)中的各類(lèi)數(shù)據(jù),以達(dá)到減少前端業(yè)務(wù)服務(wù)對(duì)數(shù)據(jù)庫(kù)的直接高并發(fā)訪問(wèn),從而提升大規(guī)模網(wǎng)站集群中動(dòng)態(tài)服務(wù)的并發(fā)訪問(wèn)能力。
生產(chǎn)場(chǎng)景的 Memcached 服務(wù)一般被用來(lái)保存網(wǎng)站中經(jīng)常被讀取的對(duì)象或數(shù)據(jù),就像我們的客戶(hù)端瀏覽器也會(huì)把經(jīng)常訪問(wèn)的網(wǎng)頁(yè)緩存起來(lái)一樣,通過(guò)內(nèi)存緩存來(lái)存取對(duì)象或數(shù)據(jù)要比磁盤(pán)存取快很多,因?yàn)榇疟P(pán)是機(jī)械的。因此,在當(dāng)今的 IT 企業(yè)中,Memcached 的應(yīng)用范圍很廣泛。
互聯(lián)網(wǎng)常見(jiàn)內(nèi)存緩存服務(wù)軟件
2.Memcached 的用戶(hù)與應(yīng)用場(chǎng)景
2.1 Memcached 常見(jiàn)用途工作流程
Memcached 是一種內(nèi)存緩存軟件,在工作中經(jīng)常用來(lái)緩存數(shù)據(jù)庫(kù)的查詢(xún)數(shù)據(jù),數(shù)據(jù)被緩存在事先與分配的 Memcached 管理的內(nèi)存中,可以通過(guò) API 或命令的方式存取內(nèi)存中緩存的這些數(shù)據(jù),Memcached 服務(wù)內(nèi)存中緩存的數(shù)據(jù)就像一張巨大的 Hash 表,每條數(shù)據(jù)都是以 Key-Value 對(duì)的形式存在。
2.2 網(wǎng)站讀取 Memcached 數(shù)據(jù)時(shí)工作流程
從邏輯上來(lái)說(shuō),當(dāng)程序訪問(wèn)后端數(shù)據(jù)庫(kù)獲取數(shù)據(jù)時(shí)會(huì)優(yōu)先訪問(wèn) Memcached 緩存,如果緩存中有數(shù)據(jù)就直接返回給客戶(hù)端用戶(hù),如果沒(méi)有合適的數(shù)據(jù)(沒(méi)有命中),再去后端的數(shù)據(jù)庫(kù)讀取數(shù)據(jù),讀取到需要的數(shù)據(jù)后,就會(huì)把數(shù)據(jù)返回給客戶(hù)端,同時(shí)還會(huì)把讀取到的數(shù)據(jù)緩存到 Memcached 內(nèi)存中,這樣客戶(hù)端用戶(hù)再次請(qǐng)求相同的數(shù)據(jù)時(shí)就會(huì)直接讀取 Memcached 緩存的數(shù)據(jù)了,這就大大地減輕了后端數(shù)據(jù)庫(kù)的壓力,并提高了整個(gè)網(wǎng)站的響應(yīng)速度,提升了用戶(hù)體驗(yàn)。
如上圖所示:使用 Memcached 緩存查詢(xún)的數(shù)據(jù)來(lái)減少數(shù)據(jù)庫(kù)壓力的具體工作流程如下:
- Web 程序首先檢查客戶(hù)端請(qǐng)求的數(shù)據(jù)是否在 Memcached 緩存中存在,如果存在,直接把請(qǐng)求的數(shù)據(jù)返回給客戶(hù)端,此時(shí)不再請(qǐng)求后端數(shù)據(jù)庫(kù)。
- 如果請(qǐng)求的數(shù)據(jù)在 Memcached 緩存中不存在,則程序會(huì)去請(qǐng)求數(shù)據(jù)庫(kù)服務(wù),把從數(shù)據(jù)庫(kù)中取到的數(shù)據(jù)返回給客戶(hù)端,同時(shí)把新取到的數(shù)據(jù)緩存一份到 Memcached 緩存中。
2.3 網(wǎng)站更新 Memcached 數(shù)據(jù)時(shí)的工作流程
(1)當(dāng)程序更新或刪除數(shù)據(jù)時(shí),會(huì)首先處理后端數(shù)據(jù)庫(kù)中的數(shù)據(jù)。
(2)在處理后端數(shù)據(jù)庫(kù)中數(shù)據(jù)的同時(shí),也會(huì)通知 Memcached,告訴它對(duì)應(yīng)的舊數(shù)據(jù)失效,從而保證 Memcached 中緩存的數(shù)據(jù)始終和數(shù)據(jù)庫(kù)中一致,這個(gè) 數(shù)據(jù)一致性 非常重要,也是大型網(wǎng)站分布式緩存集群最頭疼的問(wèn)題所在。
(3)如果是在高并發(fā)讀寫(xiě)場(chǎng)合,除了要程序通知 Memcached 過(guò)期的緩存失效外,還可能要通過(guò)相關(guān)機(jī)制,例如在數(shù)據(jù)庫(kù)上部署相關(guān)程序(如在數(shù)據(jù)庫(kù)中設(shè)置觸發(fā)器使用 UDFs),實(shí)現(xiàn)當(dāng)數(shù)據(jù)庫(kù)有更新時(shí)就把數(shù)據(jù)更新到 Memcached 服務(wù)中,這樣一來(lái),客戶(hù)端在訪問(wèn)新數(shù)據(jù)時(shí),因預(yù)先把更新過(guò)的數(shù)據(jù)庫(kù)數(shù)據(jù)復(fù)制到 Memcached 中緩存起來(lái)了,所以可以減少第一次查詢(xún)數(shù)據(jù)庫(kù)帶來(lái)的訪問(wèn)壓力,提升 Memcached 中緩存的命中率,甚至新浪門(mén)戶(hù)還會(huì)把持久化存儲(chǔ) Redis 做成 MySQL 數(shù)據(jù)庫(kù)的從庫(kù),實(shí)現(xiàn)真正的主從復(fù)制。
下圖為 Memcached 網(wǎng)站作為緩存應(yīng)用更新數(shù)據(jù)的流程
下圖為 Memcached 服務(wù)作為緩存應(yīng)用通過(guò)相關(guān)軟件更新數(shù)據(jù)的流程
2.4 Memcached 在企業(yè)中的應(yīng)用場(chǎng)景
2.4.1 作為數(shù)據(jù)庫(kù)的查詢(xún)數(shù)據(jù)緩存
(1)完整數(shù)據(jù)緩存
例如:電商的商品分類(lèi)功能不是經(jīng)常變動(dòng)的,因此可以事先放到 Memcached 里,然后再對(duì)外提供數(shù)據(jù)訪問(wèn)。這個(gè)過(guò)程被稱(chēng)之為 數(shù)據(jù)預(yù)熱。此時(shí)只需讀取緩存,無(wú)需讀取數(shù)據(jù)庫(kù)就能得到 Memcached 緩存里的所有商品分類(lèi)數(shù)據(jù)了,所以數(shù)據(jù)庫(kù)的訪問(wèn)壓力就會(huì)大大降低。
為什么商品分類(lèi)數(shù)據(jù)可以事先放在緩存里呢?因?yàn)樯唐贩诸?lèi)幾乎都是由內(nèi)部人員管理的,如果需要更新數(shù)據(jù),更新數(shù)據(jù)庫(kù)后,就可以把數(shù)據(jù)同時(shí)更新到 Memcached 里。如果把商品分類(lèi)數(shù)據(jù)做成靜態(tài)化文件,然后通過(guò)在前端 Web 緩存或者使用 CDN 加速效果更好。
(2)熱點(diǎn)數(shù)據(jù)緩存
熱點(diǎn)數(shù)據(jù)緩存一般是用于由用戶(hù)更新的商品,例如淘寶的賣(mài)家,在賣(mài)家新增商品后,網(wǎng)站程序就會(huì)把商品寫(xiě)入后端數(shù)據(jù)庫(kù),同時(shí)把這部分?jǐn)?shù)據(jù),放入 Memcached 內(nèi)存中,下一次訪問(wèn)這個(gè)商品的請(qǐng)求就直接從 Memcached 內(nèi)存中取走了。這種方法用來(lái)緩存網(wǎng)站熱點(diǎn)的數(shù)據(jù),即利用 Memcached 緩存經(jīng)常被訪問(wèn)的數(shù)據(jù)。
注:這個(gè)過(guò)程可以通過(guò)程序?qū)崿F(xiàn),也可以在數(shù)據(jù)庫(kù)上安裝相關(guān)軟件進(jìn)行設(shè)置,直接由數(shù)據(jù)庫(kù)把內(nèi)容更新到 Memcached 中,就相當(dāng)于 Memcached 是 MySQL 的從庫(kù)一樣。
- 如果碰到電商雙 11,秒殺高并發(fā)的業(yè)務(wù)場(chǎng)景,必須要事先預(yù)熱各種緩存,包括前端的 Web 緩存和后端的數(shù)據(jù)庫(kù)緩存。也就是先把數(shù)據(jù)放入內(nèi)存預(yù)熱,然后逐步動(dòng)態(tài)更新。此時(shí),會(huì)先讀取緩存,如果緩存里沒(méi)有對(duì)應(yīng)的數(shù)據(jù),再去讀取數(shù)據(jù)庫(kù),然后把讀到的數(shù)據(jù)放入緩存。如果數(shù)據(jù)庫(kù)里的數(shù)據(jù)更新,需要同時(shí)觸發(fā)緩存更新,防止給用戶(hù)過(guò)期的數(shù)據(jù),當(dāng)然對(duì)于百萬(wàn)級(jí)別并發(fā)還有很多其他的工作要做。
- 絕大多數(shù)的網(wǎng)站動(dòng)態(tài)數(shù)據(jù)都是保存在數(shù)據(jù)庫(kù)當(dāng)中的,每次頻繁地存取數(shù)據(jù)庫(kù),會(huì)導(dǎo)致數(shù)據(jù)庫(kù)性能急劇下降,無(wú)法同時(shí)服務(wù)更多的用戶(hù)(比如 MySQL 特別頻繁的鎖表就存在此問(wèn)題),那么,就可以讓 Memcached 來(lái)分擔(dān)數(shù)據(jù)庫(kù)的壓力。增加 Memcached 服務(wù)的好處除了可以分擔(dān)數(shù)據(jù)庫(kù)的壓力以外,還包括無(wú)須改動(dòng)整個(gè)網(wǎng)站架構(gòu),只須簡(jiǎn)單地修改下程序邏輯,讓程序先讀取 Memcached 緩存查詢(xún)數(shù)據(jù)即可,當(dāng)然別忘了,更新數(shù)據(jù)時(shí)也要更新 Memcached 緩存。
2.4.2 作為集群節(jié)點(diǎn)的 Session 會(huì)話共享存儲(chǔ)
即把客戶(hù)端用戶(hù)請(qǐng)求多個(gè)前端應(yīng)用服務(wù)集群產(chǎn)生的 Session 會(huì)話信息,統(tǒng)一存儲(chǔ)到一個(gè) Memcached 緩存中。由于 Session 會(huì)話數(shù)據(jù)是存儲(chǔ)在內(nèi)存中的,所以速度很快。
下圖為 Memcached 服務(wù)在企業(yè)集群架構(gòu)中的常見(jiàn)工作位置:
3.Memcached 的特點(diǎn)與工作機(jī)制
3.1 Memcached 的特點(diǎn)
- 協(xié)議簡(jiǎn)單。Memcached 的協(xié)議實(shí)現(xiàn)很簡(jiǎn)單,采用的是基于文本行的協(xié)議,能通過(guò) telnet / nc 等命令直接操作 Memcached 服務(wù)存儲(chǔ)數(shù)據(jù)。
- 支持 epoll / kqueue 異步 I/O 模型,使用 libevent 作為事件處理通知機(jī)制。簡(jiǎn)單的說(shuō),libevent 是一套利用 C 開(kāi)發(fā)的程序庫(kù),它將 BSD 系統(tǒng)的 kqueue,Linux 系統(tǒng)的 epoll 等事件處理功能封裝成一個(gè)接口,確保即使服務(wù)器端的連接數(shù)增加也能發(fā)揮很好的性能。Memcached 就是利用這個(gè) libevent 庫(kù)進(jìn)行異步事件處理的。
- 采用 key / value 鍵值數(shù)據(jù)類(lèi)型。被緩存的數(shù)據(jù)以 key / value 鍵值形式存在。
- 全內(nèi)存緩存,效率高。Memcached 管理內(nèi)存的方式非常高效,即全部的數(shù)據(jù)都存放于 Memcached 服務(wù)事先分配好的內(nèi)存中,無(wú)持久化存儲(chǔ)的設(shè)計(jì),和系統(tǒng)的物理內(nèi)存一樣,當(dāng)重啟系統(tǒng)或 Memcached 服務(wù)時(shí),Memcached 內(nèi)存中的數(shù)據(jù)就會(huì)丟失。如果希望重啟后,數(shù)據(jù)依然能保留,那么就可以采用 Redis 這樣的持久性?xún)?nèi)存緩存系統(tǒng)。
- 當(dāng)內(nèi)存中緩存的數(shù)據(jù)容量達(dá)到服務(wù)啟動(dòng)時(shí)設(shè)定的內(nèi)存值時(shí),就會(huì)自動(dòng)使用 LRU 算法(最近最少被使用的)刪除過(guò)期的緩存數(shù)據(jù)。也可以在存放數(shù)據(jù)時(shí)對(duì)存儲(chǔ)的數(shù)據(jù)設(shè)置過(guò)期時(shí)間,這樣過(guò)期后數(shù)據(jù)就自動(dòng)被清除,Memcached 服務(wù)本身不會(huì)監(jiān)控?cái)?shù)據(jù)過(guò)期,而是在訪問(wèn)的時(shí)候查看 key 的時(shí)間戳判斷是否過(guò)期。
- 可支持分布式集群。Memcached 沒(méi)有像 MySQL 那樣的主從復(fù)制方式,分布式 Memcached 集群的不同服務(wù)器之間是互不通信的,每一個(gè)節(jié)點(diǎn)都獨(dú)立存取數(shù)據(jù),并且數(shù)據(jù)內(nèi)容也不一樣。通過(guò)對(duì) Web 應(yīng)用端的程序設(shè)計(jì)或者通過(guò)支持 Hash 算法的負(fù)載均衡軟件,可以讓 Memcached 支持大規(guī)模海量分布式緩存集群應(yīng)用。
3.2 Memcached 工作原理與機(jī)制
3.2.1 Memcached 工作原理
Memcached 是一套類(lèi)似 C/S 模式架構(gòu)的軟件,在服務(wù)器端啟動(dòng) Memcached 服務(wù)守護(hù)進(jìn)程,可以指定監(jiān)聽(tīng)本地的IP地址,端口號(hào),并發(fā)訪問(wèn)連接數(shù),以及分配了多少內(nèi)存來(lái)處理客戶(hù)端請(qǐng)求。
3.2.2 Socket 時(shí)間處理機(jī)制
Memcached 軟件是由 C 語(yǔ)言來(lái)實(shí)現(xiàn)的,全部代碼僅有 2000 多行,采用的是異步 epoll / kqueue 非阻塞 I/O 網(wǎng)絡(luò)模型,其實(shí)現(xiàn)方式是基于異步的 libevent 事件單進(jìn)程,單線程模式。使用 libevent 作為事件通知機(jī)制,應(yīng)用程序端通過(guò)指定服務(wù)器的 IP 地址及端口,就可以連接 Memcached 服務(wù)進(jìn)行通信。
3.2.3 數(shù)據(jù)存儲(chǔ)機(jī)制
需要被緩存的數(shù)據(jù)以 key / value 鍵值對(duì)的形式保存在服務(wù)器端預(yù)分配的內(nèi)存區(qū)中,每個(gè)被緩存的數(shù)據(jù)都有唯一的標(biāo)識(shí) key,操作 Memcached 中的數(shù)據(jù)就是通過(guò)這個(gè)唯一標(biāo)識(shí)的 key 進(jìn)行的。緩存到 Memcached 中的數(shù)據(jù)僅放置在 Memcached 服務(wù)預(yù)分配的內(nèi)存中,而非存儲(chǔ)在 Memcached 服務(wù)器所在的磁盤(pán)上,因此存取速度非常快。
由于 Memcached 服務(wù)自身沒(méi)有對(duì)緩存的數(shù)據(jù)進(jìn)行持久化存儲(chǔ)的涉及,因此,在服務(wù)器端的 Memcached 服務(wù)進(jìn)程重啟之后,存儲(chǔ)在內(nèi)存中的這些數(shù)據(jù)就會(huì)丟失。且當(dāng)內(nèi)存中緩存的數(shù)據(jù)容量達(dá)到啟動(dòng)時(shí)設(shè)定的內(nèi)存值時(shí),也會(huì)自動(dòng)使用 LRU 算法刪除過(guò)期的數(shù)據(jù)。
開(kāi)發(fā) Memcached 的初衷?xún)H是通過(guò)內(nèi)存緩存提升訪問(wèn)效率,并沒(méi)有過(guò)多考慮數(shù)據(jù)的永久存儲(chǔ)問(wèn)題。因此,如果使用 Memcached 作為緩存數(shù)據(jù)服務(wù),要考慮數(shù)據(jù)丟失后帶來(lái)的問(wèn)題,例如:是否可以重新生成數(shù)據(jù),還有,在高并發(fā)場(chǎng)合下緩存宕機(jī)或重啟會(huì)不會(huì)導(dǎo)致大量請(qǐng)求直接到數(shù)據(jù)庫(kù),導(dǎo)致數(shù)據(jù)庫(kù)無(wú)法承受,最終導(dǎo)致網(wǎng)站架構(gòu)雪崩等。
3.2.4 內(nèi)存管理機(jī)制
- 采用 Slab 內(nèi)存分配機(jī)制
- 采用 LRU 對(duì)象清除機(jī)制
- 采用 Hash 機(jī)制快速檢索 Item
3.2.5 多線程處理機(jī)制
- 多線程處理時(shí)采用的是 pthread(POSIX)線程模式。
- 若要激活多線程,可在編譯時(shí)指定:
./configure --enable-threads
- 鎖機(jī)制不夠完善
- 負(fù)載過(guò)重時(shí),可以開(kāi)啟多線程(-t 線程數(shù)為CPU核數(shù))
3.3 Memcached 預(yù)熱理念及集群節(jié)點(diǎn)的正確重啟方法
當(dāng)需要大面積重啟 Memcached 時(shí),首先要在前端控制網(wǎng)站入口的訪問(wèn)流量,然后,重啟 Memcached 集群并進(jìn)行數(shù)據(jù)預(yù)熱,所有數(shù)據(jù)都預(yù)熱完畢之后,再逐步放開(kāi)前端網(wǎng)站入口的流量。
為了滿(mǎn)足 Memcached 服務(wù)數(shù)據(jù)可以持久化存儲(chǔ)的需求,在較早時(shí)期,新浪網(wǎng)基于 Memcached 服務(wù)開(kāi)發(fā)了一款 NoSQL 軟件,名字為 MemcacheDB,實(shí)現(xiàn)了在緩存的基礎(chǔ)上增加了持久存儲(chǔ)的特性,不過(guò)目前逐步被更優(yōu)秀的 Redis 軟件取代了。
如果由于機(jī)房斷電或者搬遷服務(wù)器集群到新機(jī)房,那么啟動(dòng)集群服務(wù)器時(shí),一定要從網(wǎng)站集群的后端依次往前端開(kāi)啟,特別是開(kāi)啟 Memcached 緩存服務(wù)器時(shí)要提前預(yù)熱。
4.Memcached 內(nèi)存管理
4.1 Memcached 內(nèi)存管理機(jī)制解析
4.1.1 malloc 內(nèi)存管理機(jī)制
在講解 Memcached 內(nèi)存管理機(jī)制前,先來(lái)了解 malloc。malloc 的全稱(chēng)是 memory allocation
,即動(dòng)態(tài)內(nèi)存分配,當(dāng)無(wú)法知道內(nèi)存具體位置的時(shí)候,想要綁定真正的內(nèi)存空間,就需要用到動(dòng)態(tài)分配內(nèi)存。
早期的 Memcached 內(nèi)存管理是通過(guò) malloc 分配內(nèi)存實(shí)現(xiàn)的,使用完后通過(guò) free 來(lái)回收內(nèi)存。這種方式容易產(chǎn)生內(nèi)存碎片并降低操作系統(tǒng)對(duì)內(nèi)存的管理效率。因此,也會(huì)加重操作系統(tǒng)內(nèi)存管理器的負(fù)擔(dān),最壞的情況下,會(huì)導(dǎo)致操作系統(tǒng)比 Memcached 進(jìn)程本身還慢,為了解決上述問(wèn)題,Slab Allocator
內(nèi)存分配機(jī)制就誕生了。
4.1.2 Slab 內(nèi)存管理機(jī)制
現(xiàn)在的 Memcached 是利用 Slab Allocation
機(jī)制來(lái)分配和管理內(nèi)存的,過(guò)程如下:
提前將大內(nèi)存分配大小為 1MB 的若干個(gè) slab,然后針對(duì)每個(gè) slab 再進(jìn)行小對(duì)象填充,這個(gè)小對(duì)象稱(chēng)為 chunk,避免大量重復(fù)的初始化和清理,減輕了內(nèi)存管理器的負(fù)擔(dān)。
Slab Allocation
內(nèi)存分配的原理是按照預(yù)先規(guī)定的大小,將分配給 Memcached 服務(wù)的內(nèi)存預(yù)先分割成特定長(zhǎng)度的內(nèi)存塊(chunk),再把尺寸相同的內(nèi)存塊(chunk)分成組(chunks slab class),這些內(nèi)存塊不會(huì)釋放,可以重復(fù)利用,如下圖所示。
新增數(shù)據(jù)對(duì)象存儲(chǔ)時(shí)。因 Memcached 服務(wù)器中保存著 slab 內(nèi)空閑 chunk 的列表,他會(huì)根據(jù)該列表選擇 chunk,然后將數(shù)據(jù)緩存于其中。當(dāng)有數(shù)據(jù)存入時(shí),Memcached 根據(jù)接收到的數(shù)據(jù)大小,選擇最適合數(shù)據(jù)大小的 slab 分配一個(gè)能存下這個(gè)數(shù)據(jù)的最小內(nèi)存塊(chunk)。例如:有 100 字節(jié)的一個(gè)數(shù)據(jù),就會(huì)被分配存入下面 112 字節(jié)的一個(gè)內(nèi)存塊中,這樣會(huì)有 12 字節(jié)被浪費(fèi),這部分空間就不能被使用了,這也是 Slab Allocator 機(jī)制的一個(gè)缺點(diǎn)。
4.1.3 Slab Allocation 的主要術(shù)語(yǔ)
Slab Allocation 主要術(shù)語(yǔ) | 注解說(shuō)明 |
---|---|
slab class | 內(nèi)存區(qū)類(lèi)別(48 byte - 1MB) |
slab | 動(dòng)態(tài)創(chuàng)建的實(shí)際內(nèi)存區(qū),即分配給 Slab 的內(nèi)存空間,默認(rèn)是 1MB。分配給 Slab 之后根據(jù) slab 的大小切分成 chunk。slab 默認(rèn)大小為 1048576 byte (1MB)。大于 1MB 的數(shù)據(jù)會(huì)忽略 |
slab classid | Slab class 的 ID |
chunk | 數(shù)據(jù)區(qū)塊,固定大小,chunk 初始大小,1.4 版本中是 48 bytes |
item | 實(shí)際存儲(chǔ)在 chunk 中的數(shù)據(jù)項(xiàng) |
4.1.4 Slab 內(nèi)存管理機(jī)制特點(diǎn)
提前分配大內(nèi)存 Slab 1MB,再進(jìn)行小對(duì)象填充 chunk。
避免大量重復(fù)的初始化和清理,減輕內(nèi)存管理器負(fù)擔(dān)。
避免頻繁 malloc / free 內(nèi)存分配導(dǎo)致的碎片。
4.1.5 MC 內(nèi)存管理機(jī)制小結(jié)
MC 的早期內(nèi)存管理機(jī)制為 malloc(動(dòng)態(tài)內(nèi)存分配)。
malloc(動(dòng)態(tài)內(nèi)存分配)產(chǎn)生內(nèi)存碎片,導(dǎo)致操作系統(tǒng)性能急劇下降。
Slab 內(nèi)存分配機(jī)制可以解決內(nèi)存碎片的問(wèn)題。
Memcached 服務(wù)的內(nèi)存預(yù)先分割成特定長(zhǎng)度的內(nèi)存塊,稱(chēng)為 chunk,用于緩存數(shù)據(jù)的內(nèi)存空間或內(nèi)存塊,相當(dāng)于磁盤(pán)的 block,只不過(guò)磁盤(pán)的每一個(gè) block 都是相等的,而 chunk 只有在同一個(gè) Slab Class 內(nèi)才是相等的。
Slab Class 指特定大?。?MB)的包含多個(gè) chunk 的集合或組,一個(gè) Memcached 包含多個(gè) Slab Class,每個(gè) Slab Class 包含多個(gè)相同大小的 chunk。
Slab 機(jī)制也有缺點(diǎn),例如,Chunk 的空間會(huì)有浪費(fèi)等。
4.2 Memcached Slab Allocator 內(nèi)存管理機(jī)制的缺點(diǎn)
(1)chunk 存儲(chǔ) item 浪費(fèi)空間
Slab Allocator 解決了當(dāng)初的內(nèi)存碎片問(wèn)題,但新的機(jī)制也給 Memcached 帶來(lái)了新的問(wèn)題。這個(gè)問(wèn)題就是,由于分配的是特定長(zhǎng)度的內(nèi)存,因此無(wú)法有效利用分配的內(nèi)存。例如,將 100 字節(jié)的數(shù)據(jù)緩存到 128 字節(jié)的 chunk 中,剩余的 28 字節(jié)就浪費(fèi)了,如下圖所示:
避免浪費(fèi)內(nèi)存的辦法是,預(yù)先計(jì)算出應(yīng)用存入的數(shù)據(jù)大小,或把同一業(yè)務(wù)類(lèi)型的數(shù)據(jù)存入一個(gè) Memcached 服務(wù)器中,確保存入的數(shù)據(jù)大小相對(duì)均勻,這樣就可以減少內(nèi)存的浪費(fèi)。
還有一種辦法是,在啟動(dòng)時(shí)指定 -f
參數(shù),能在某種程度上控制內(nèi)存組之間的大小差異。在應(yīng)用中使用 Memcached 時(shí),通常可以不重新設(shè)置這個(gè)參數(shù),即使用默認(rèn)值 1.25 進(jìn)行部署即可。如果想優(yōu)化 Memcached 對(duì)內(nèi)存的使用,可以考慮重新計(jì)算數(shù)據(jù)的預(yù)期平均長(zhǎng)度,調(diào)整這個(gè)參數(shù)來(lái)獲得合適的設(shè)置值,命令如下:
-f <factor>chunk size growth factor (default:1.25)!
(2)Slab 尾部剩余空間
假設(shè)在 classid=40 中,兩個(gè) chunk 占用了 1009384 byte,那么就有 1048576 - 1009384 = 39192 byte 會(huì)被浪費(fèi)掉。解決辦法:規(guī)劃 slab 大小 = chunk 大小 * n 整數(shù)倍。
4.3 使用 Growth Factor 對(duì) Slab Allocator 內(nèi)存管理機(jī)制調(diào)優(yōu)
在啟動(dòng) Memcached 時(shí)指定 Growth Factor 因子(通過(guò) -f
選項(xiàng)),就可以在某種程度上控制每組 Slab 之間的差異。默認(rèn)值 1.25。但是,在該選項(xiàng)出現(xiàn)之前,這個(gè)因子曾經(jīng)被固定為 2。讓我們用以前的設(shè)置,以 verbose 模式啟動(dòng) Memcached 試試看:
memcached -f 2 w
啟動(dòng)后的 verbose
slab class 1:chunk size 128 perslab 8192
slab class 2:chunk size 256 perslab 4096
slab class 3:chunk size 512 perslab 2048
slab class 4:chunk size 1024 perslab 1024
slab class 5:chunk size 2048 perslab 512
slab class 6:chunk size 4096 perslab 256
slab class 7:chunk size 8192 perslab 128
slab class 8:chunk size 16384 perslab 64
slab class 9:chunk size 32768 perslab 32
slab class 10:chunk size 65536 perslab 16
slab class 11:chunk size 131072 perslab 8
slab class 12:chunk size 262144 perslab 4
slab class 13:chunk size 524288 perslab 2
可見(jiàn),從 128 字節(jié)的組開(kāi)始,組的大小依次增大為原來(lái)的 2 倍。這樣設(shè)置的問(wèn)題是,Slab 之間的差別比較大,有些情況下就相當(dāng)浪費(fèi)內(nèi)存。因此,為盡量減少內(nèi)存浪費(fèi),兩年前追加了 growth factor 這個(gè)選項(xiàng)。
來(lái)看看現(xiàn)在的默認(rèn)設(shè)置(f=1.25
)時(shí)的輸出:
slab class 1:chunk size 88 perslab 11915 <---88*11915=1048520
slab class 2:chunk size 112 perslab 9362
slab class 3:chunk size 144 perslab 7281
slab class 4:chunk size 184 perslab 5698
slab class 5:chunk size 232 perslab 4519
slab class 6:chunk size 296 perslab 3542
slab class 7:chunk size 376 perslab 2788
slab class 8:chunk size 472 perslab 2221
slab class 9:chunk size 592 perslab 1771
slab class 10:chunk size 744 perslab 1409 <---744*1409=1048520
此時(shí)每個(gè) Slab 的大小是一樣的,即 1048520,1MB。組間的差距比因子為 2 時(shí)小得多,可見(jiàn),這個(gè)值越小,Slab 中 chunk size 的差距就越小,內(nèi)存浪費(fèi)也就越小??梢?jiàn),默認(rèn)值 1.25 更適合緩存幾百字節(jié)的對(duì)象。從上面的輸出結(jié)果來(lái)看,可能會(huì)覺(jué)得有些計(jì)算誤差,這些誤差是為了保持字節(jié)數(shù)的對(duì)齊而故意設(shè)置的。
當(dāng)使用 Memcached 或是直接使用默認(rèn)值進(jìn)行部署時(shí),最好是重新計(jì)算一下數(shù)據(jù)的預(yù)期平均長(zhǎng)度,調(diào)整 growth factor,以獲得最恰當(dāng)?shù)脑O(shè)置。內(nèi)存是珍貴的資源,浪費(fèi)就太可惜了。
4.4 Memcached 的檢測(cè)過(guò)期與刪除機(jī)制
(1)Memcached 懶惰檢測(cè)對(duì)象過(guò)期機(jī)制
首先要知道,Memcached 不會(huì)主動(dòng)檢測(cè) item 對(duì)象是否過(guò)期,而是在進(jìn)行 get 操作時(shí)檢查 item 對(duì)象是否過(guò)期以及是否應(yīng)該刪除!
因?yàn)椴粫?huì)主動(dòng)檢測(cè) item 對(duì)象是否過(guò)期,自然也就不會(huì)釋放已分配給對(duì)象的內(nèi)存空間了,除非為添加的數(shù)據(jù)設(shè)定過(guò)期時(shí)間或內(nèi)存緩存滿(mǎn)了,在數(shù)據(jù)過(guò)期后,客戶(hù)端不能通過(guò) key 取出它的值,其存儲(chǔ)空間將被重新利用。
Memcached 使用的這種策略為 懶惰檢測(cè)對(duì)象過(guò)期策略,即自己不監(jiān)控存入的 key / value 對(duì)是否過(guò)期,而是在獲取 key 值時(shí)查看記錄的時(shí)間戳(sed key flag exptime bytes),從而檢查 key / value 對(duì)空間是否過(guò)期。這種策略不會(huì)在過(guò)期檢測(cè)上浪費(fèi) CPU 資源。
(2)Memcached 懶惰刪除對(duì)象機(jī)制
當(dāng)刪除 item 對(duì)象時(shí),一般不會(huì)釋放內(nèi)存空間,而是做刪除標(biāo)記,將指針?lè)湃?slot 回收插槽,下次分配的時(shí)候直接使用。
Memcached 在分配空間時(shí),會(huì)優(yōu)先使用已經(jīng)過(guò)期的 key / value 對(duì)空間;若分配的內(nèi)存空間占滿(mǎn),Memcached 就會(huì)使用 LRU 算法來(lái)分配空間,刪除最近最少使用的 key / value 對(duì),從而將其空間分配給新的 key / value 對(duì)。在某些情況下(完整緩存),如果不想使用 LRU 算法,那么可以通過(guò) -M
參數(shù)來(lái)啟動(dòng) Memcached,這樣,Memcached 在內(nèi)存耗盡時(shí),會(huì)返回一個(gè)報(bào)錯(cuò)信息,如下:
-M rerurn error on memory exhausted(rather than removing items)
(3)下面針對(duì) Memcached 刪除機(jī)制進(jìn)行一個(gè)小結(jié)文章來(lái)源:http://www.zghlxwxcb.cn/news/detail-758377.html
- 不主動(dòng)檢測(cè) item 對(duì)象是否過(guò)期,而是在 get 時(shí)才會(huì)檢查 item 對(duì)象是否過(guò)期以及是否應(yīng)該刪除。
- 當(dāng)刪除 item 對(duì)象時(shí),一般不釋放內(nèi)存空間,而是做刪除標(biāo)記,將指針?lè)湃?slot 回收插槽,下次分配的時(shí)候直接使用。
- 當(dāng)內(nèi)存空間滿(mǎn)的時(shí)候,將會(huì)根據(jù) LRU 算法把最近最少使用的 item 對(duì)象刪除。
- 數(shù)據(jù)存入可以設(shè)定過(guò)期時(shí)間,但是數(shù)據(jù)過(guò)期后不會(huì)被立即刪除,而是在 get 時(shí)檢查 item 對(duì)象是否過(guò)期以及是否應(yīng)該刪除。
- 如果不希望系統(tǒng)使用 LRU 算法清除數(shù)據(jù),可以用使用
-M
參數(shù)。
參考:Memcached 數(shù)據(jù)庫(kù)緩存文章來(lái)源地址http://www.zghlxwxcb.cn/news/detail-758377.html
到了這里,關(guān)于【分布式緩存】一文詳解 Memcached的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!