1.为什么要使用memcache


 由于网站的高并发读写需求,传统的关系型数据库开始出现瓶颈,例如:

1)对数据库的高并发读写

关系型数据库本身就是个庞然大物,处理过程非常耗时(如解析SQL语句,事务处理等)。如果对关系型数据库进行高并发读写(每秒上万次的访问),那么它是无法承受的。

2)对海量数据的处理

对于大型的SNS网站,每天有上千万次的苏剧产生(如twitter, 新浪微博)。对于关系型数据库,如果在一个有上亿条数据的数据表种查找某条记录,效率将非常低。


使用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小块,如下图所示:


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字节对齐):