memcached的学习(15)

2018.6.21
关于memcached的总结:
参考博客:https://blog.csdn.net/initphp/article/details/44893869

Memcache的网络模型

1.Memcached主要是基于Libevent 网络事件库进行开发的。

2.Memcached的网络模型分为两部分:主线程和工作线程。主线程主要用来接收客户端的连接信息;工作线程主要用来接管客户端连接,处理具体的业务逻辑。默认情况下会开启8个工作线程。

3.主线程和工作线程之间主要是通过pipe管道来进行通信。当主线程接收到客户端的连接的时候,会通过轮询的方式选择一个工作线程,然后向该工作线程的管道pipe写数据。工作线程监听到管道中有数据写入的时候,就会触发代码逻辑去接管客户端的连接。

4.每个工作线程也是基于Libevent的事件机制,当客户端有数据写入的时候,就会触发读取的操作。
详细的源码解读还是请看网络模型这一章节,下面是一张Memcached的网络模型图:
在这里插入图片描述

Memcached的命令解析和消息回应

1 每一个客户端连接都会维护一个struct conn的数据结构。该结构主要用来保存客户端的连接相关信息,以及读取客户端数据的buf结构,以及消息回应的iov和msghdr数据结构。

2.Memcached的命令是通过\n符号来进行分割的。当接收到一串命令之后,会去检查rbuf中是否有\n符号,如果没有,则继续等待客户端的数据到来;如果有\n符号,则去解析这个命令。

3.每个命令行命令的参数,都是通过空格符号分割的。例如:get username。通过空格符号,将命令分解成N(N小于8)个部分,然后放入tokens的一个数组中。然后就可以通过不同的操作命令,来调用不同的业务逻辑。

4.Memcached的消息回应主要是通过封装iov和msghdr的数据结构,调用sendmsg方法来向客户端发送数据。客户端也是通过\n的符号来分隔命令行。

具体的源码解析,需要详细看命令解析和消息回应的章节。
看一下conn结构中比较重要的几个参数:
在这里插入图片描述
然后看一下整个命令解析和消息回应的流程图:
在这里插入图片描述

Memcached的缓存存储

1.Memcached的缓存存储的基本单元是struct item的数据结构。每一个item都包含了存储缓存的key,key的长度,value值,value值的长度,缓存有效期,最近访问时间等信息。

2.Memcached在初始化的时候,会初始化一个slabclass_t结构的数组(slabclass)。slabclass是数据空间,规定了不同长度的item会存储在不同的slabclass_t的结构上面。例如slabclass[0]最大存储96byte,slabclass[1]最大存储120byte,slabclass[2]最大存储150byte,则当一个item的大小为135byte的时候,会存储在slabclass[2]上面。

3.Memcached每次分配内存都是以slab为基础单位。默认情况下,slab的大小为1M。slabclass在初始化的时候,Memcached会给每一个slabclass_t的数据结构分配一个1M大小的slab。slab主要是用来存储item数据的,当slab被分配了之后,就会根据每个slabclass_t所能存储的最大size(slabclass_t->size),来切分成N个item,并且将这部分item放入slabclass_t的空闲列表(*slots),当需要使用item的时候就会从这个空闲里列表中取一个。

4.每个slabclass_t分配的item大小都是根据slabclass_t->size来设置的,真正存储的时候实际item的大小可能会小于slabclass_t->size的情况。浪费主要是为了减少内存碎片,内存管理起来更加合理。

5.Memcached在读取数据的时候,需要依赖于HashTable。当每次创建一个Item的时候,都会将item的地址挂载到HashTable上去。客户端就可以通过key来查询到对应的item地址了。

6.每个使用中的item都会通过prev和next被挂载到LRU链表上。每个未使用的item都会通过prev和next放入slabclass的空闲链表*slots上。

7.当memcached需要使用item的时候,先去LRU链表尾部查询是否有过期的item,有的话则直接使用过期的item;如果没有,则取slabclass的空闲链表上获取一个待使用的item(如果空闲链表为空,则会分配一个1M的slab放入slabclass上,并且填充空闲链表)。如果内存分配失败,则开始开启LRU淘汰策略,就会从LRU链中强制淘汰一个item。

详细的源码解读还得看前面的文章,Memcached的增删改查操作,HashTable,LRU,缓存存储机制slabs
看一下Item数据结构:

  1. //item的具体结构    
  2. typedef struct _stritem {    
  3.     //记录下一个item的地址,主要用于LRU链和freelist链    
  4.     struct _stritem *next;    
  5.     //记录下一个item的地址,主要用于LRU链和freelist链    
  6.     struct _stritem *prev;    
  7.     //记录HashTable的下一个Item的地址    
  8.     struct _stritem *h_next;    
  9.     //最近访问的时间,只有set/add/replace等操作才会更新这个字段    
  10.     //当执行flush命令的时候,需要用这个时间和执行flush命令的时间相比较,来判断是否失效    
  11.     rel_time_t      time;       /* least recent access */    
  12.     //缓存的过期时间。设置为0的时候,则永久有效。    
  13.     //如果Memcached不能分配新的item的时候,设置为0的item也有可能被LRU淘汰    
  14.     rel_time_t      exptime;    /* expire time */    
  15.     //value数据大小    
  16.     int             nbytes;     /* size of data */    
  17.     //引用的次数。通过这个引用的次数,可以判断item是否被其它的线程在操作中。    
  18.     //也可以通过refcount来判断当前的item是否可以被删除,只有refcount -1 = 0的时候才能被删除    
  19.     unsigned short  refcount;    
  20.     uint8_t         nsuffix;    /* length of flags-and-length string */    
  21.     uint8_t         it_flags;   /* ITEM_* above */    
  22.     //slabs_class的ID。    
  23.     uint8_t         slabs_clsid;/* which slab class we're in */    
  24.     uint8_t         nkey;       /* key length, w/terminating null and padding */    
  25.     /* this odd type prevents type-punning issues when we do  
  26.      * the little shuffle to save space when not using CAS. */    
  27.     //数据存储结构    
  28.     union {    
  29.         uint64_t cas;    
  30.         char end;    
  31.     } data[];    
  32.     /* if it_flags & ITEM_CAS we have 8 bytes CAS */    
  33.     /* then null-terminated key */    
  34.     /* then " flags length\r\n" (no terminating null) */    
  35.     /* then data with terminating \r\n (no terminating null; it's binary!) */    
  36. } item;    

slabclass的数据结构:

  1. //slabclass的结构    
  2. typedef struct {    
  3.     //当前的slabclass存储最大多大的item    
  4.     unsigned int size;    
  5.     //每一个slab上可以存储多少个item.每个slab大小为1M, 可以存储的item个数根据size决定。    
  6.     unsigned int perslab;    
  7.     
  8.     //当前slabclass的(空闲item列表)freelist 的链表头部地址    
  9.     //freelist的链表是通过item结构中的item->next和item->prev连建立链表结构关系    
  10.     void *slots;           /* list of item ptrs */    
  11.     //当前总共剩余多少个空闲的item    
  12.     //当sl_curr=0的时候,说明已经没有空闲的item,需要分配一个新的slab(每个1M,可以切割成N多个Item结构)    
  13.     unsigned int sl_curr;   /* total free items in list */    
  14.     
  15.     //总共分配多少个slabs    
  16.     unsigned int slabs;     /* how many slabs were allocated for this class */    
  17.     //分配的slab链表    
  18.     void **slab_list;       /* array of slab pointers */    
  19.     unsigned int list_size; /* size of prev array */    
  20.     
  21.     unsigned int killing;  /* index+1 of dying slab, or zero if none */    
  22.     //总共请求的总bytes    
  23.     size_t requested; /* The number of requested bytes */    
  24. } slabclass_t;    
  25. //定义一个slabclass数组,用于存储最大200个的slabclass_t的结构。    
  26. static slabclass_t slabclass[MAX_NUMBER_OF_SLAB_CLASSES];    

通过item的size来选择slab_class的数据存储空间:
在这里插入图片描述
slabclass和slab、item以及free list之间的关系:
在这里插入图片描述
LRU链,主要用于内存不够分配的时候,进行item的淘汰:
在这里插入图片描述
HashTable,Memcahced主要通过HashTable来查询Item的地址:
在这里插入图片描述

Memcached使用的注意事项:

1.存储的数据尽量小于10K,因为数据太大,容易导致LRU淘汰比较严重,而且Memcached是CPU密集型的程序,容易降低QPS

2.分布式Memcached部署的时候,要注意热点Key

3.可以通过存储索引、拆分大结构数据等方式,存储比较小的缓存数据

4.memcached的缓存会有LRU强制淘汰,所以设置了缓存不过期,在内存分配完了之后也会造成强制淘汰的情况。

5.memcached的key有长度限制,小于200个字节

6.存储的数据不能超过1M,最好小于10K

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值