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使用实例:

  1. <?php
  2. $mc = new Memcache();
  3. $mc->connect('127.0.0.1', 11211);
  4. $uid = (int)$_GET['uid'];
  5. $sql = "select * from users where uid='uid' ";
  6. $key = md5($sql);
  7. if(!($data = $mc->get($key))) {
  8. $conn = mysql_connect('localhost', 'test', 'test');
  9. mysql_select_db('test');
  10. $result = mysql_fetch_object($result);
  11. while($row = mysql_fetch_object($result)) {
  12. $data[] = $row;
  13. }
  14. $mc->add($key, $datas);
  15. }
  16. var_dump($datas);
  17. ?>

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文件中):

(注:memcache的github项目地址:https://github.com/wusuopubupt/memcached)

  1. /**
  2. * Determines the chunk sizes and initializes the slab class descriptors
  3. * accordingly.
  4. */
  5. void slabs_init(const size_t limit, const double factor, const bool prealloc) {
  6. int i = POWER_SMALLEST - 1;
  7. unsigned int size = sizeof(item) + settings.chunk_size;
  8. mem_limit = limit;
  9. if (prealloc) {
  10. /* Allocate everything in a big chunk with malloc 通过malloc的方式申请内存*/
  11. mem_base = malloc(mem_limit);
  12. if (mem_base != NULL) {
  13. mem_current = mem_base;
  14. mem_avail = mem_limit;
  15. } else {
  16. fprintf(stderr, "Warning: Failed to allocate requested memory in"
  17. " one large chunk.\nWill allocate in smaller chunks\n");
  18. }
  19. }
  20. memset(slabclass, 0, sizeof(slabclass));
  21. while (++i < POWER_LARGEST && size <= settings.item_size_max / factor) {
  22. /* Make sure items are always n-byte aligned  注意这里的字节对齐*/
  23. if (size % CHUNK_ALIGN_BYTES)
  24. size += CHUNK_ALIGN_BYTES - (size % CHUNK_ALIGN_BYTES);
  25. slabclass[i].size = size;
  26. slabclass[i].perslab = settings.item_size_max / slabclass[i].size;
  27. size *= factor;//以1.25为倍数增大chunk
  28. if (settings.verbose > 1) {
  29. fprintf(stderr, "slab class %3d: chunk size %9u perslab %7u\n",
  30. i, slabclass[i].size, slabclass[i].perslab);
  31. }
  32. }
  33. power_largest = i;
  34. slabclass[power_largest].size = settings.item_size_max;
  35. slabclass[power_largest].perslab = 1;
  36. if (settings.verbose > 1) {
  37. fprintf(stderr, "slab class %3d: chunk size %9u perslab %7u\n",
  38. i, slabclass[i].size, slabclass[i].perslab);
  39. }
  40. /* for the test suite:  faking of how much we've already malloc'd */
  41. {
  42. char *t_initial_malloc = getenv("T_MEMD_INITIAL_MALLOC");
  43. if (t_initial_malloc) {
  44. mem_malloced = (size_t)atol(t_initial_malloc);
  45. }
  46. }
  47. if (prealloc) {
  48. slabs_preallocate(power_largest);
  49. }
  50. }

上面代码中的slabclass是一个类型为slabclass_t结构的数组,其定义如下:

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

借用别人的一张图说明slabclass_t结构:

由分割算法的源代码可知,slab算法按照不同大小的chunk分割slab页,而不同大小的chunk以factor(默认是1.25)倍增大。

使用memcache -u root -vv 命令查看内存分配情况(8字节对齐):

找到大小最合适的chunk分配给请求缓存的数据:

  1. /*
  2. * Figures out which slab class (chunk size) is required to store an item of
  3. * a given size.
  4. *
  5. * Given object size, return id to use when allocating/freeing memory for object
  6. * 0 means error: can't store such a large object
  7. */
  8. unsigned int slabs_clsid(const size_t size) {
  9. int res = POWER_SMALLEST;// 初始化为最小的chunk
  10. if (size == 0)
  11. return 0;
  12. while (size > slabclass[res].size) //逐渐增大chunk size,直到找到第一个比申请的size大的chunk
  13. if (res++ == power_largest)     /* won't fit in the biggest slab */
  14. return 0;
  15. return res;
  16. }

内存分配:

(此处参考:http://slowsnail.com.cn/?p=20

  1. static void *do_slabs_alloc(const size_t size, unsigned int id) {
  2. slabclass_t *p;
  3. void *ret = NULL;
  4. item *it = NULL;
  5. if (id < POWER_SMALLEST || id > power_largest) {//判断id是否会导致slabclass[]数组越界
  6. MEMCACHED_SLABS_ALLOCATE_FAILED(size, 0);
  7. return NULL;
  8. }
  9. p = &slabclass[id];//获取slabclass[id]的引用
  10. assert(p->sl_curr == 0 || ((item *)p->slots)->slabs_clsid == 0);//判断slabclass[id]是否有剩余的chunk
  11. if (! (p->sl_curr != 0 || do_slabs_newslab(id) != 0)) {//如果slabclass[id]中已经没有空余chunk并且试图向系统申请一个“页”(slab)的chunk失败,则返回NULL
  12. /* We don't have more memory available */
  13. ret = NULL;
  14. } else if (p->sl_curr != 0) {//slabclass[id]的空闲链表中还有chunk,则直接将其分配出去
  15. it = (item *)p->slots;//获取空闲链表的头指针
  16. p->slots = it->next;//将头结点指向下一个结点(取下头结点)
  17. if (it->next) it->next->prev = 0;//将新头结点的prev指针置空
  18. p->sl_curr--;//减少slabclass[id]空闲链表中的chunk计数
  19. ret = (void *)it;//将头结点赋给ret指针
  20. }
  21. if (ret) {//请求成功
  22. p->requested += size;//更新slabclass[id]所分配的内存总数
  23. MEMCACHED_SLABS_ALLOCATE(size, id, p->size, ret);
  24. } else {
  25. MEMCACHED_SLABS_ALLOCATE_FAILED(size, id);
  26. }
  27. return ret;
  28. }

do_slabs_allc()函数首先尝试从slot列表(被回收的chunk)中获取可用的chunk,如果有可用的就返回,否则从空闲的chunk列表中获取可用的chunk并返回。

删除过期item:

延迟删除过期item到查找时进行,可以提高memcache的效率,因为不必每时每刻检查过期item,从而提高CPU工作效率

使用LRU(last recently used)算法淘汰数据:

  1. /*
  2. * try to get one off the right LRU
  3. * don't necessariuly unlink the tail because it may be locked: refcount>0
  4. * search up from tail an item with refcount==0 and unlink it; give up after 50
  5. * tries
  6. */
  7. if (tails[id] == 0) {
  8. itemstats[id].outofmemory++;
  9. return NULL;
  10. }
  11. for (search = tails[id]; tries > 0 && search != NULL; tries--, search=search->prev) {
  12. if (search->refcount == 0) { //refount==0的情况,释放掉
  13. if (search->exptime == 0 || search->exptime > current_time) {
  14. itemstats[id].evicted++;
  15. itemstats[id].evicted_time = current_time - search->time;
  16. STATS_LOCK();
  17. stats.evictions++;
  18. STATS_UNLOCK();
  19. }
  20. do_item_unlink(search);
  21. break;
  22. }
  23. }
  24. it = slabs_alloc(ntotal, id);
  25. if (it == 0) {
  26. itemstats[id].outofmemory++;
  27. /* Last ditch effort. There is a very rare bug which causes
  28. * refcount leaks. We've fixed most of them, but it still happens,
  29. * and it may happen in the future.
  30. * We can reasonably assume no item can stay locked for more than
  31. * three hours, so if we find one in the tail which is that old,
  32. * free it anyway.
  33. */
  34. tries = 50;
  35. for (search = tails[id]; tries > 0 && search != NULL; tries--, search=search->prev) {
  36. if (search->refcount != 0 && search->time + 10800 < current_time) { //最近3小时没有被访问到的情况,释放掉
  37. itemstats[id].tailrepairs++;
  38. search->refcount = 0;
  39. do_item_unlink(search);
  40. break;
  41. }
  42. }
  43. it = slabs_alloc(ntotal, id);
  44. if (it == 0) {
  45. return NULL;
  46. }
  47. }

从item列表的尾部开始遍历,找到refcount==0的chunk,调用do_item_unlink()函数释放掉,另外,search->time+10800<current_time(即最近3小时没有被访问过的item),也释放掉--这就是LRU算法的原理。

深入理解Memcache原理 [转]的更多相关文章

  1. node.js学习(三)简单的node程序&&模块简单使用&&commonJS规范&&深入理解模块原理

    一.一个简单的node程序 1.新建一个txt文件 2.修改后缀 修改之后会弹出这个,点击"是" 3.运行test.js 源文件 使用node.js运行之后的. 如果该路径下没有该 ...

  2. Atitit 图像处理 深刻理解梯度原理计算.v1 qc8

    Atitit 图像处理 深刻理解梯度原理计算.v1 qc8 1.1. 图像处理  梯度计算  基本梯度 内部梯度 外部梯度 方向梯度1 2. 图像梯度就是图像边缘吗?2 1.1. 图像处理  梯度计算 ...

  3. 深入理解PHP原理之变量作用域

    26 Aug 08 深入理解PHP原理之变量作用域(Scope in PHP)   作者: Laruence(   ) 本文地址: http://www.laruence.com/2008/08/26 ...

  4. 深入理解PHP原理之变量分离/引用

    19 Sep 08 深入理解PHP原理之变量分离/引用(Variables Separation) 作者: Laruence(   ) 本文地址: http://www.laruence.com/20 ...

  5. 《深入理解mybatis原理》 MyBatis事务管理机制

    MyBatis作为Java语言的数据库框架,对数据库的事务管理是其很重要的一个方面.本文将讲述MyBatis的事务管理的实现机制. 首先介绍MyBatis的事务Transaction的接口设计以及其不 ...

  6. 《深入理解mybatis原理》 Mybatis初始化机制具体解释

    对于不论什么框架而言.在使用前都要进行一系列的初始化,MyBatis也不例外. 本章将通过下面几点具体介绍MyBatis的初始化过程. 1.MyBatis的初始化做了什么 2. MyBatis基于XM ...

  7. 《深入理解mybatis原理》 MyBatis的架构设计以及实例分析

    作者博客:http://blog.csdn.net/u010349169/article/category/2309433 MyBatis是目前非常流行的ORM框架,它的功能很强大,然而其实现却比较简 ...

  8. 轻松理解Redux原理及工作流程

    轻松理解Redux原理及工作流程 Redux由Dan Abramov在2015年创建的科技术语.是受2014年Facebook的Flux架构以及函数式编程语言Elm启发.很快,Redux因其简单易学体 ...

  9. 深入理解mybatis原理, Mybatis初始化SqlSessionFactory机制详解(转)

    文章转自http://blog.csdn.net/l454822901/article/details/51829785 对于任何框架而言,在使用前都要进行一系列的初始化,MyBatis也不例外.本章 ...

随机推荐

  1. QRCode二维码生成

    pom配置 <dependency> <groupId>com.github.cloudecho</groupId> <artifactId>qrcod ...

  2. 编译FFmpeg成一个SO库<转>

    转帖地址:http://www.ihubin.com/blog/android-ffmpeg-demo-3/ ============================================= ...

  3. BitLocker 加密工具挂起和恢复命令行(windows7)

    如果你的硬盘使用BitLocker加密了,但是有时候需要高效率的硬盘做某些事情,可以暂时挂起加密,命令行如下方便做个bat. 挂起: manage-bde -protectors -disable C ...

  4. Web应用程序项目XX已配置为使用IIS

    今天在看开源项目Umbraco是,出现一个项目加载不了,并报如下错误: Web应用程序项目Umbraco.Cms.Web.UI已配置为使用IIS.若要访问本地IIS网站,必须安装下列IIS组件..,如 ...

  5. This implementation is not part of the Windows Platform FIPS validated cryptographic algorithms while caching 问题及解决

    一.背景    情节1:做别的测试安装下载了软件,妈蛋结果下了百度各种捆绑软件,之后一一卸载,清洁.    情节2:做完上述动作重启电脑后,有线连接连不上,尴尬,然后下载驱动,升级之后ok了. 二.问 ...

  6. 解决phalcon读取mysql乱码

    原先的项目用的是phalcon,迁移到新服务器上面后中文字符变为'?',即便连接参数设置了charset => 'utf8'也无效,一开始怀疑是版本问题,后来直接拷过去完全一样的库也没用:最后还 ...

  7. Matlab Map

    http://blog.csdn.net/yuzhiyuxia/article/details/7305225 >> weekmap = containers.Map({'Monday', ...

  8. Java编写最大公约数和最小公倍数

    package javaapplication24; class NegativeIntegerException extends Exception{ String message; public ...

  9. CSS实现DIV水平自适应居中

    DIV水平自适应居中 <!DOCTYPE html> <html lang="cn"> <head> <meta charset=&quo ...

  10. [转载]Js小技巧||给input type=“password”的输入框赋默认值

    http://www.cnblogs.com/Raywang80s/archive/2012/12/06/2804459.html [转载]Js小技巧||给input type="passw ...