memcached深度分析(5)

发表于:2015-07-10来源:uml.org.cn作者:火龙果软件点击数: 标签:数据库
Memcached的内存管理方式是非常精巧和高效的,它很大程度上减少了直接alloc系统内存的次数,降低函数开销和内存碎片产生几率,虽然这种方式会造成一些

  Memcached的内存管理方式是非常精巧和高效的,它很大程度上减少了直接alloc系统内存的次数,降低函数开销和内存碎片产生几率,虽然这种方式会造成一些冗余浪费,但是这种浪费在大型系统应用中是微不足道的。

  Memcached的理论参数计算方式

  影响 memcached 工作的几个参数有:

  常量REALTIME_MAXDELTA 60*60*24*30

  最大30天的过期时间

  conn_init()中的freetotal(=200)

  最大同时连接数

  常量KEY_MAX_LENGTH 250

  最大键长

  settings.factor(=1.25)

  factor将影响chunk的步进大小

  settings.maxconns(=1024)

  最大软连接

  settings.chunk_size(=48)

  一个保守估计的key+value长度,用来生成id1中的chunk长度(1.2)。id1的chunk长度等于这个数值加上item结构体的长度(32),即默认的80字节。

  常量POWER_SMALLEST 1

  最小classid(1.2)

  常量POWER_LARGEST 200

  最大classid(1.2)

  常量POWER_BLOCK 1048576

  默认slab大小

  常量CHUNK_ALIGN_BYTES (sizeof(void *))

  保证chunk大小是这个数值的整数倍,防止越界(void *的长度在不同系统上不一样,在标准32位系统上是4)

  常量ITEM_UPDATE_INTERVAL 60

  队列刷新间隔

  常量LARGEST_ID 255

  最大item链表数(这个值不能比最大的classid小)

  变量hashpower(在1.1中是常量HASHPOWER)

  决定hashtable的大小

  根据上面介绍的内容及参数设定,可以计算出的一些结果:

  1、在memcached中可以保存的item个数是没有软件上限的,之前我的100万的说法是错误的。

  2、假设NewHash算法碰撞均匀,查找item的循环次数是item总数除以hashtable大小(由hashpower决定),是线性的。

  3、Memcached限制了可以接受的最大item是1MB,大于1MB的数据不予理会。

  4、Memcached的空间利用率和数据特性有很大的关系,又与DONT_PREALLOC_SLABS常量有关。 在最差情况下,有198个slab会被浪费(所有item都集中在一个slab中,199个id全部分配满)。

  Memcached的定长优化

  根据上面几节的描述,多少对memcached有了一个比较深入的认识。在深入认识的基础上才好对它进行优化。

  Memcached本身是为变长数据设计的,根据数据特性,可以说它是“面向大众”的设计,但是很多时候,我们的数据并不是这样的“普遍”,典型的情况中,一种是非均匀分布,即数据长度集中在几个区域内(如保存用户 Session);另一种更极端的状态是等长数据(如定长键值,定长数据,多见于访问、在线统计或执行锁)。

  这里主要研究一下定长数据的优化方案(1.2),集中分布的变长数据仅供参考,实现起来也很容易。

  解决定长数据,首先需要解决的是slab的分配问题,第一个需要确认的是我们不需要那么多不同chunk长度的slab,为了最大限度地利用资源,最好chunk和item等长,所以首先要计算item长度。

  在之前已经有了计算item长度的算法,需要注意的是,除了字符串长度外,还要加上item结构的长度32字节。

  假设我们已经计算出需要保存200字节的等长数据。

  接下来是要修改slab的classid和chunk长度的关系。在原始版本中,chunk长度和classid是有对应关系的,现在如果把所有的chunk都定为200个字节,那么这个关系就不存在了,我们需要重新确定这二者的关系。一种方法是,整个存储结构只使用一个固定的id,即只使用199个槽中的1个,在这种条件下,就一定要定义DONT_PREALLOC_SLABS来避免另外的预分配浪费。另一种方法是建立一个hash关系,来从item确定classid,不能使用长度来做键,可以使用key的NewHash结果等不定数据,或者直接根据key来做hash(定长数据的key也一定等长)。这里简单起见,选择第一种方法,这种方法的不足之处在于只使用一个id,在数据量非常大的情况下,slab链会很长(因为所有数据都挤在一条链上了),遍历起来的代价比较高。

  前面介绍了三种空间冗余,设置chunk长度等于item长度,解决了第一种空间浪费问题,不预申请空间解决了第二种空间浪费问题,那么对于第一种问题(slab内剩余)如何解决呢,这就需要修改POWER_BLOCK常量,使得每一个slab大小正好等于chunk长度的整数倍,这样一个slab就可以正好划分成n个chunk。这个数值应该比较接近1MB,过大的话同样会造成冗余,过小的话会造成次数过多的alloc,根据chunk长度为200,选择1000000作为POWER_BLOCK的值,这样一个slab就是100万字节,不是1048576。三个冗余问题都解决了,空间利用率会大大提升。

原文转自:http://www.uml.org.cn/sjjm/201411134.asp