天天看點

九爺帶你了解 深入了解 Memcache 原理深入了解Memcache原理

深入了解Memcache原理

1.為什麼要使用memcache

 由于網站的高并發讀寫需求,傳統的關系型資料庫開始出現瓶頸,例如:

1)對資料庫的高并發讀寫:

關系型資料庫本身就是個龐然大物,處理過程非常耗時(如解析SQL語句,事務處理等)。如果對關系型資料庫進行高并發讀寫(每秒上萬次的通路),那麼它是無法承受的。

2)對海量資料的處理:

對于大型的SNS網站,每天有上千萬次的蘇劇産生(如twitter, 新浪微網誌)。對于關系型資料庫,如果在一個有上億條資料的資料表種查找某條記錄,效率将非常低。

使用memcache能很好的解決以上問題。

在實際使用中,通常把資料庫查詢的結果儲存到Memcache中,下次通路時直接從memcache中讀取,而不再進行資料庫查詢操作,這樣就在很大程度上減少了資料庫的負擔。

儲存在memcache中的對象實際放置在記憶體中,這也是memcache如此高效的原因。

九爺帶你了解 深入了解 Memcache 原理深入了解Memcache原理

2.memcache的安裝和使用

這個網上有太多教程了,不做贅言。

3.基于libevent的事件處理

libevent是個程式庫,它将Linux的epoll、BSD類作業系統的kqueue等事件處理功能 封裝成統一的接口。即使對伺服器的連接配接數增加,也能發揮O(1)的性能。

 memcached使用這個libevent庫,是以能在Linux、BSD、Solaris等作業系統上發揮其高性能。 

參考:

4.memcache使用執行個體:

<?php
$mc = new Memcache();
$mc->connect('127.0.0.1', 11211);

$uid = (int)$_GET['uid'];
$sql = "select * from users where uid='uid' ";
$key = md5($sql);
if(!($data = $mc->get($key))) {
    $conn = mysql_connect('localhost', 'test', 'test');
    mysql_select_db('test');
    $result = mysql_fetch_object($result);
    while($row = mysql_fetch_object($result)) {
          $data[] = $row;
    }
    $mc->add($key, $datas);
}

var_dump($datas);
?>
      

5.memcache如何支援高并發

memcache使用多路複用I/O模型,如(epoll, select等),傳統I/O中,系統可能會因為某個使用者連接配接還沒做好I/O準備而一直等待,知道這個連接配接做好I/O準備。這時如果有其他使用者連接配接到伺服器,很可能會因為系統阻塞而得不到響應。

而多路複用I/O是一種消息通知模式,使用者連接配接做好I/O準備後,系統會通知我們這個連接配接可以進行I/O操作,這樣就不會阻塞在某個使用者連接配接。是以,memcache才能支援高并發。

此外,memcache使用了多線程機制。可以同時處理多個請求。線程數一般設定為CPU核數,這研報告效率最高。

6.使用Slab配置設定算法儲存資料

slab配置設定算法的原理是:把固定大小(1MB)的記憶體分為n小塊,如下圖所示:

九爺帶你了解 深入了解 Memcache 原理深入了解Memcache原理

slab配置設定算法把每1MB大小的記憶體稱為一個slab頁,每次向系統申請一個slab頁,然後再通過分隔算法把這個slab頁分割成若幹個小塊的chunk(如上圖所示),然後把這些chunk配置設定給使用者使用,分割算法如下(在slabs.c檔案中):

    memset(slabclass, 0, sizeof(slabclass));
    // 初始化每個slabclass_t的trunk大小和每個slab中trunk數量
    // slabclass中每個slabclass_t的trunk大小增長為factor倍
    // 注意 i 從索引 1 開始
    while (++i < POWER_LARGEST && size <= settings.item_size_max / factor) {
        /* Make sure items are always n-byte aligned */
        if (size % CHUNK_ALIGN_BYTES)                             // 記憶體8位元組對齊
            size += CHUNK_ALIGN_BYTES - (size % CHUNK_ALIGN_BYTES);

        slabclass[i].size = size;
        slabclass[i].perslab = settings.item_size_max / slabclass[i].size;
        size *= factor;
        if (settings.verbose > 1) {
            fprintf(stderr, "slab class %3d: chunk size %9u perslab %7u\n",
                    i, slabclass[i].size, slabclass[i].perslab);
        }
    }

    // slabclass中最後一個slabclass_t的trunk大小設定為最大item大小
    power_largest = i;
    slabclass[power_largest].size = settings.item_size_max;
    slabclass[power_largest].perslab = 1;
    if (settings.verbose > 1) {
        fprintf(stderr, "slab class %3d: chunk size %9u perslab %7u\n",
                i, slabclass[i].size, slabclass[i].perslab);
    }
    ....// 省略
}      

上面代碼中的slabclass是一個類型為slabclass_t結構的數組,其定義如下:

typedef struct {
    unsigned int size;      /* sizes of items */
    unsigned int perslab;   /* how many items per slab */
    void **slots;           /* list of item ptrs */
    unsigned int sl_total;  /* size of previous array */
    unsigned int sl_curr;   /* first free slot */
    void *end_page_ptr;         /* pointer to next free item at end of page, or 0 */
    unsigned int end_page_free; /* number of items remaining at end of last alloced page */
    unsigned int slabs;     /* how many slabs were allocated for this class */
    void **slab_list;       /* array of slab pointers */
    unsigned int list_size; /* size of prev array */
    unsigned int killing;  /* index+1 of dying slab, or zero if none */
    size_t requested; /* The number of requested bytes */
} slabclass_t;      

由分割算法的源代碼可知,slab算法按照不同大小的chunk分割slab頁,而不同大小的chunk以factor(預設是1.25)倍增大。

使用memcache -vv 指令檢視記憶體配置設定情況(8位元組對齊):

九爺帶你了解 深入了解 Memcache 原理深入了解Memcache原理