Key-Value存储作为NoSQL存储的一种常见方式,提供了比SQL数据库更好的可扩展性和
读写性能
。比如当前开源最热门的Memcached和Redis;淘宝的Tair、腾讯的Cmem、
Amazon的Dynamo等等,无论是做缓存还是持久存储,均使用内存作为主要存储介质,故内存管理策略就显得尤为重要了,是影响性能的重要因素。
这里从源代码层面对Memcached、Redis和UDC(腾讯以前用的一套KV持久化存储系统)的内存管理策略进行分析,3者的内存管理策略各不相同,其他KV系统也和这3种方法大同小异了。最后对这3种策略进行了实际的性能测试分析,有出入的请使劲拍砖!
--------------------------------------------------------------------------
1 MemCached:Slab Allocation机制
3个
主要的
概念:
1 Chunk块:固定大小的数据块,数据存储的基本单位,1个Chunk
块
存1个数据,剩余空间不做其他用途
2 Slab页:固定大小的内存块(页),申请内存的基本单位,默认为1MB,每个SlabClass会把申请的Slab切分成相同大小Chunk来存数据
3 SlabClass:由Chunk的大小确定,是大小相同的所有Chunk块集合
如上图所示,Memcahed启动时,
会根据传入的-n(最小数据尺寸,默认48B),-f(增长因子,默认为1.25)启动选项初始化SlabClass。默认情况下,首个SlabClass的Chunk大小为80B(32B元数据头+48B最小数据尺寸),然后以1.25为比值生成等比数列,直到1MB(1个Slab页大小)为止。生成的SlabClass如下所示(perslab值为每个Slab页能分割出的Chunk个数):
$ memcached -vv
slab class 1: chunk size 80 perslab 13107
slab class 2: chunk size 104 perslab 10082
slab class 3: chunk size 136 perslab 7710
.....
slab class 41: chunk size 717184 perslab 1
slab class 42: chunk size 1048576 perslab 1
当用户发来请求时,Memcahed
会根据
key+value
的值(能存放在1个Chunk内)来判断属于哪个SlabClass
。确定这个SlabClass
有无空闲的Chunk块,没有的话则先给这个SlabClass
申请一个Slab
页,
将该Slab页按
本SlabClass
的Chunk
块大小进行切割,然后分配1个来存放用户数据。(这里还有LRU算法淘汰旧数据的逻辑,就不放在这里分析)。
这种策略的特点:
- 实现较复杂
- 参数的选择(最小数据尺寸,增长因子)直接影响性能及内存利用率
- 每个数据存放于1个Chunk块,读写简单
相关源代码:主要由slab.h/c(SlabClass
和Slab
相关)、item.h/
c(Chunk块相关)这几个文件实现
*
SlabClass数据结构:
* 初始化
SlabClass(通过增长因子计算各个SlabClass的Chunk块大小等)
* 为SlabClass[i]申请一块新Slab内存页