memcached指南file:///C:/Users/Administrator/Desktop/簡(jiǎn)歷/memcached-筆記資料/memcached權(quán)威指南.pdf
底層認(rèn)識(shí)在p351頁(yè)
Memcached如何支持高并發(fā)
Memcached使用多路復(fù)用I/O模型(epoll,select等,具體為memcached采用的是libevent庫(kù)统捶,該庫(kù)在linux下就是用的epoll)敲街。傳統(tǒng)阻塞I/O中,系統(tǒng)可能會(huì)因?yàn)槟硞€(gè)用戶連接還沒(méi)做好I/O準(zhǔn)備而一直等待注整,直到這個(gè)連接做好I/O準(zhǔn)備(這時(shí)如果這時(shí)有其他用戶連接到服務(wù)器,很可能因?yàn)橄到y(tǒng)阻塞而得不到響應(yīng))。多路復(fù)用I/O模型熊杨,阻塞不發(fā)生在具體執(zhí)行I/O的,而是epoll或select上盗舰,當(dāng)某個(gè)鏈接準(zhǔn)備好IO時(shí)晶府,epoll就返回這個(gè)連接的信息,進(jìn)而去執(zhí)行相應(yīng)連接的IO,這樣大大提升了CPU效率钻趋。此外memcached使用了多線程模式川陆,在開(kāi)啟memcahed服務(wù)器時(shí)可以通過(guò) -t指定線程數(shù)(線程數(shù)不是越多越好,一般指定為cpu核數(shù)就好了)蛮位。
使用Slab分配算法保存數(shù)據(jù)
該算法在slabs.c源文件中较沪, 我們觀察的這個(gè)函數(shù)slab_init(),具體就是初始化這個(gè)Memcached的內(nèi)存的結(jié)構(gòu)的。最后生成了一個(gè)數(shù)組失仁,其中每個(gè)元素為slabclass_t這個(gè)結(jié)構(gòu)體尸曼。該結(jié)構(gòu)體就描述了具體每個(gè)slab class的信息(哪些塊被使用了啊,每個(gè)chunk大小為多少等)萄焦。每個(gè)slab class的維持的內(nèi)存總共都是1048576個(gè)字節(jié)(1M)控轿。每個(gè)數(shù)組的chunk都是靠著一個(gè)增長(zhǎng)因子成倍增長(zhǎng)變化的的。最后一個(gè)slab class的大小每個(gè)chunk就是1M(跳出while循環(huán)拂封,再設(shè)置組后一個(gè)slab class的chunk帶大小的)茬射。
有趣的事,知道為什么每個(gè)memcached實(shí)例為什么分配那么多個(gè)slab class嗎冒签?源碼中本來(lái)最多可以分201個(gè)slab class的躲株,但是分配的那個(gè)while循環(huán),一個(gè)條件是<201镣衡,另一個(gè)是每個(gè)slab class的size要小于一個(gè)POWER_BLOCK(這個(gè)常量就是規(guī)定的每個(gè)slab class的總大小)的一半霜定。因此我們可以每次啟動(dòng)的時(shí)候看到屏幕上的消息档悠,明白為什么是分配那么多個(gè)(通常倒數(shù)第二個(gè)的大小塊接近1M的一半)。
我們都知道惰性刪除和未過(guò)期數(shù)據(jù)被踢現(xiàn)象望浩。那么這些現(xiàn)象又是怎么實(shí)現(xiàn)的呢辖所?
刪除過(guò)期item
惰性刪除是因?yàn)閕tem的刪除機(jī)制放在item的訪問(wèn)的函數(shù)中(do_item_get_notedeleted)的,通過(guò)比較item的過(guò)期時(shí)間和當(dāng)前時(shí)間磨德,前者小的話缘回,就刪除這個(gè)item。因此item到期后不是立馬從內(nèi)存中刪除典挑,而是訪問(wèn)這個(gè)item時(shí)如果到期了酥宴,才把item從內(nèi)存中刪除。
使用LRU淘汰算法
大多數(shù)關(guān)于memcached都說(shuō)您觉,當(dāng)memcached使用的內(nèi)存數(shù)大于設(shè)置的最大內(nèi)存數(shù)時(shí)拙寡,比如20kb的slabclass都已經(jīng)使用完了chunk,這時(shí)新來(lái)的數(shù)據(jù)還要再插入20kb的slabclass琳水,會(huì)啟動(dòng)LRU算法試著淘汰最近最少使用的數(shù)據(jù)肆糕,給新數(shù)據(jù)騰位置。
有趣的事是我知道什么情況下在孝,LRU騰位置失敗诚啃,以及memcached的LRU的實(shí)現(xiàn)。Memcached的LRU實(shí)現(xiàn)顯然不能完全滿足操作系統(tǒng)原理課介紹的那般準(zhǔn)確私沮。它就是先從你需要插入的slab class的數(shù)據(jù)項(xiàng)列表從后往前遍歷(因?yàn)閙emcached會(huì)把剛剛訪問(wèn)過(guò)的item放到item列表頭部)始赎,尋找一個(gè)沒(méi)有被引用(其引用計(jì)數(shù)器為0,[這item被引用的總數(shù)])的仔燕,找到的話极阅,就把這數(shù)據(jù)項(xiàng)所占內(nèi)存騰出來(lái)。如果沒(méi)找到的話涨享,就去找一個(gè)3個(gè)小時(shí)沒(méi)有訪問(wèn)過(guò)的item(其上次訪問(wèn)的時(shí)間+3個(gè)小時(shí)的秒數(shù)小于現(xiàn)在的時(shí)間)筋搏,滿足就釋放其內(nèi)存,騰出來(lái)給新的元素用厕隧。如果上面兩個(gè)步驟都沒(méi)有找到的話奔脐,就宣布這個(gè)新item不能插入因?yàn)樯暾?qǐng)內(nèi)存失敗。
Memcached多線程模型
Memcahed是一個(gè)多線程的緩存服務(wù)器程序吁讨。在Memcached內(nèi)部髓迎,線程分為:主線程(接受客戶端鏈接,并把連接分配給工作線程處理)建丧;工作線程(處理客戶端連接的請(qǐng)求)
具體行為是主線程主要偵聽(tīng)客戶端的連接drive_machine()排龄,有客戶端連接到memcached時(shí),memcached會(huì)調(diào)用accept函數(shù)接受到來(lái)的連接翎朱, 當(dāng)主線程接收到客戶端連接后橄维,便通過(guò)dispatch_conn_new函數(shù)把客戶端連接注冊(cè)到libevent進(jìn)行偵聽(tīng)(放到多路復(fù)用中尺铣。這里采用的是libevent庫(kù),而libevent庫(kù)在linux下用的是epoll模型争舞,并指定以后處理該連接的讀寫請(qǐng)求的工作線程)凛忿,然后libevent會(huì)偵聽(tīng)到客戶端的讀寫請(qǐng)求,有請(qǐng)求來(lái)了就把任務(wù)push到某個(gè)工作線程的CQ隊(duì)列中竞川,并向那個(gè)工作線程發(fā)送一個(gè)信號(hào)店溢,以調(diào)用相應(yīng)的工作線程執(zhí)行函數(shù) thread_libevent_process。