程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> .NET網頁編程 >> C# >> 關於C# >> Memcached深度分析

Memcached深度分析

編輯:關於C#
 

Memcached是danga.com(運營LiveJournal的技術團隊)開發的一套分布式內存對象緩存系統,用於在動態系統中減少數據庫負載, 提升性能。關於這個東西,相信很多人都用過,本文意在通過對memcached的實現及代碼分析,獲得對這個出色的開源軟件更深入的了解,並可以根據我們 的需要對其進行更進一步的優化。末了將通過對BSM_Memcache擴展的分析,加深對memcached的使用方式理解。 本文的部分內容可能需要比較好的數學基礎作為輔助。◎Memcached是什麼 在闡述這個問題之前,我們首先要清楚它“不是什麼”。很多人把它當作和SharedMemory那種形式的存儲載體來使用,雖然memcached使用了 同樣的“Key=>Value”方式組織數據,但是它和共享內存、APC等本地緩存有非常大的區別。Memcached是分布式的,也就是說它不是 本地的。它基於網絡連接(當然它也可以使用localhost)方式完成服務,本身它是一個獨立於應用的程序或守護進程(Daemon方式)。 Memcached使用libevent庫實現網絡連接服務,理論上可以處理無限多的連接,但是它和Apache不同,它更多的時候是面向穩定的持續連接 的,所以它實際的並發能力是有限制的。在保守情況下memcached的最大同時連接數為200,這和Linux線程能力有關系,這個數值是可以調整的。 關於libevent可以參考相關文檔。 Memcached內存使用方式也和APC不同。APC是基於共享內存和MMAP的,memcachd有自己的內存分配算法和管理方式,它和共享內存沒有 關系,也沒有共享內存的限制,通常情況下,每個memcached進程可以管理2GB的內存空間,如果需要更多的空間,可以增加進程數。◎Memcached適合什麼場合 在很多時候,memcached都被濫用了,這當然少不了對它的抱怨。我經常在論壇上看見有人發貼,類似於“如何提高效率”,回復是“用memcached”,至於怎麼用,用在哪裡,用來干什麼一句沒有。memcached不是萬能的,它也不是適用在所有場合。 Memcached是“分布式”的內存對象緩存系統,那麼就是說,那些不需要“分布”的,不需要共享的,或者干脆規模小到只有一台服務器的應用, memcached不會帶來任何好處,相反還會拖慢系統效率,因為網絡連接同樣需要資源,即使是UNIX本地連接也一樣。 在我之前的測試數據中顯示,memcached本地讀寫速度要比直接PHP內存數組慢幾十倍,而APC、共享內存方式都和直接數組差不多。可見,如果只是 本地級緩存,使用memcached是非常不劃算的。 Memcached在很多時候都是作為數據庫前端cache使用的。因為它比數據庫少了很多SQL解析、磁盤操作等開銷,而且它是使用內存來管理數據的, 所以它可以提供比直接讀取數據庫更好的性能,在大型系統中,訪問同樣的數據是很頻繁的,memcached可以大大降低數據庫壓力,使系統執行效率提升。 另外,memcached也經常作為服務器之間數據共享的存儲媒介,例如在SSO系統中保存系統單點登陸狀態的數據就可以保存在memcached中,被 多個應用共享。 需要注意的是,memcached使用內存管理數據,所以它是易失的,當服務器重啟,或者memcached進程中止,數據便會丟失,所以 memcached不能用來持久保存數據。很多人的錯誤理解,memcached的性能非常好,好到了內存和硬盤的對比程度,其實memcached使用 內存並不會得到成百上千的讀寫速度提高,它的實際瓶頸在於網絡連接,它和使用磁盤的數據庫系統相比,好處在於它本身非常“輕”,因為沒有過多的開銷和直接 的讀寫方式,它可以輕松應付非常大的數據交換量,所以經常會出現兩條千兆網絡帶寬都滿負荷了,memcached進程本身並不占用多少CPU資源的情況。◎Memcached的工作方式 以下的部分中,讀者最好能准備一份memcached的源代碼。 Memcached是傳統的網絡服務程序,如果啟動的時候使用了-d參數,它會以守護進程的方式執行。創建守護進程由daemon.c完成,這個程序只有一個daemon函數,這個函數很簡單(如無特殊說明,代碼以1.2.1為准):


CODE:

#include <fcntl.h> #include <stdlib.h> #include <unistd.h> int daemon(nochdir, noclose) int nochdir, noclose; { int fd; switch (fork()) { case -1: return (-1); case 0: break; default: _exit(0); } if (setsid() == -1) return (-1); if (!nochdir) (void)chdir("/"); if (!noclose && (fd = open("/dev/null", O_RDWR, 0)) != -1) { (void)dup2(fd, STDIN_FILENO); (void)dup2(fd, STDOUT_FILENO); (void)dup2(fd, STDERR_FILENO); if (fd > STDERR_FILENO) (void)close(fd); } return (0); }

這個函數 fork 了整個進程之後,父進程就退出,接著重新定位 STDIN 、 STDOUT 、 STDERR 到空設備, daemon 就建立成功了。 Memcached 本身的啟動過程,在 memcached.c 的 main 函數中順序如下: 1 、調用 settings_init() 設定初始化參數 2 、從啟動命令中讀取參數來設置 setting 值 3 、設定 LIMIT 參數 4 、開始網絡 socket 監聽(如果非 socketpath 存在)( 1.2 之後支持 UDP 方式) 5 、檢查用戶身份( Memcached 不允許 root 身份啟動) 6 、如果有 socketpath 存在,開啟 UNIX 本地連接(Sock 管道) 7 、如果以 -d 方式啟動,創建守護進程(如上調用 daemon 函數) 8 、初始化 item 、 event 、狀態信息、 hash 、連接、 slab 9 、如設置中 managed 生效,創建 bucket 數組 10 、檢查是否需要鎖定內存頁 11 、初始化信號、連接、刪除隊列 12 、如果 daemon 方式,處理進程 ID 13 、event 開始,啟動過程結束, main 函數進入循環。 在 daemon 方式中,因為 stderr 已經被定向到黑洞,所以不會反饋執行中的可見錯誤信息。 memcached.c 的主循環函數是 drive_machine ,傳入參數是指向當前的連接的結構指針,根據 state 成員的狀態來決定動作。 Memcached 使用一套自定義的協議完成數據交換,它的 protocol 文檔可以參考: http://code.sixapart.com/svn/memcached/trunk/server/doc/protocol.txt 在API中,換行符號統一為/r/n◎Memcached的內存管理方式 Memcached有一個很有特色的內存管理方式,為了提高效率,它使用預申請和分組的方式管理內存空間,而並不是每次需要寫入數據的時候去malloc,刪除數據的時候free一個指針。Memcached使用slab->chunk的組織方式管理內存。 1.1和1.2的slabs.c中的slab空間劃分算法有一些不同,後面會分別介紹。 Slab可以理解為一個內存塊,一個slab是memcached一次申請內存的最小單位,在memcached中,一個slab的大小默認為 1048576字節(1MB),所以memcached都是整MB的使用內存。每一個slab被劃分為若干個chunk,每個chunk裡保存一個 item,每個item同時包含了item結構體、key和value(注意在memcached中的value是只有字符串的)。slab按照自己的 id分別組成鏈表,這些鏈表又按id掛在一個slabclass數組上,整個結構看起來有點像二維數組。slabclass的長度在1.1中是21,在 1.2中是200。 slab有一個初始chunk大小,1.1中是1字節,1.2中是80字節,1.2中有一個factor值,默認為1.25 在1.1中,chunk大小表示為初始大小*2^n,n為classid,即:id為0的slab,每chunk大小1字節,id為1的slab,每 chunk大小2字節,id為2的slab,每chunk大小4字節……id為20的slab,每chunk大小為1MB,就是說id為20的slab裡 只有一個chunk:

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved