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块大小等)

watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQveXl5aXJhbg==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast" alt="">


* 为SlabClass[i]申请一块新Slab内存页



---------------------------------------------------------------------------

2 Redis:简单mallc/free封装

相比Memcached。Redis做了一些优化。包含支持数据持久化(AOF和RDB两种持久化方式),支持主从复制读写分离,支持更丰富的数据结构(string、hash、set、list等)。

然而在内存管理方面,相比于Memcached的Slab Allocation机制。Redis的实现就显得简单粗暴得多了。就仅仅是mallc/free的简单封装(屏蔽底层差异,加入相关元数据等)。

这样的策略的特点:
  • 简单易实现,不易出错
  • 内存的利用率高
  • 大量系统调用开销大
  • 导致内存碎片。加重操作系统内存管理器负担
相关源码:由zmalloc.h/c这两个文件实现,相对简单

*
申请内存



*
释放内存



---------------------------------------------------------------------------

3 UDC:切分Shm成固定长度的块,组织空暇块链和用户数据块链

UDC是腾讯曾经用的一套基于Shm的、全Cache的键值存储系统。和MemCached或Redis使用进程堆内存不一样,UDC使用共享内存来存储数据,一方面进程Core的时候数据还在,一方面利于其它进程做数据落地、同步等操作。



如上图所看到的,UDC启动时会申请一块大Shm(LinkTable),然后将这块Shm切割成固定长度的Chunk块(默认200B)。通过下标组织成一条空暇块链。当收到加入数据的请求时,从空暇块链中不断取出空暇块存放用户数据,直到存放完毕形成一条用户数据块链。链头下标存放于索引层中(基于Shm的多阶Hash)。当收到删除数据的请求时。直接将相应的用户数据块链清零,插入空暇块链中就可以。

这样的策略的特点:
  • 实现较复杂
  • 代码不健壮有写乱Shm块链的风险
  • Shm数据块大小的选择直接影响性能及内存利用率
  • 用户数据块链组织成用户数据需多一次拷贝影响性能
相关源码:主要由hash_cache.h/c这2个文件实现

* 初始化LinkTable



*
Set()操作,加入用户数据

watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQveXl5aXJhbg==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast" alt="">


*
Get()操作,获取用户数据



---------------------------------------------------------------------------

性能測试:

实验前提:
* 机器:Intel(R) Xeon(R) CPU X3440 @2.53GHz (单线程。这里仅仅用到1核),8G内存
* key为uint32_t,值为 [1, 50w] 的随机数
* value为string,串长度为 [50, 500] 之间的随机字节数

1 不同块大小选择对UDC性能的影响:


实验方案:
* 对不同的块大小,进行1000w次读+写操作(即每次1个Set加1个Get)。计算总耗时和内存利用率

* 开足够大的Shm(400MB)保证能存下全部数据



实验数据:
* 块大小与总耗时(单位:s)的关系:

*
块大小与内存利用率的关系:

watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQveXl5aXJhbg==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast" alt="">



实验结论:
* 内存块越大,系统性能越好,但内存利用率越低。

须要在这2着间取个平衡点

* 一般取值为"平均(key+value)长度/2",且非常easy理解,用户数据长度基本一致即波动范围小时,内存碎片会减低,内存利用率会添加

2 UDC、Memcache、Redis内存管理策略性能对照

实验方案:
* UDC策略使用默认块大小为200B,Memcached策略使用默认增长因子1.25
* 分别进行10w、50w.... 5千万、1亿次压測操作,每次进行1个Set加1个Get。计算总耗时和内存利用率

实验数据:
* 3种内存管理策略在不同压測次数下的总耗时(单位:s)情况

watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQveXl5aXJhbg==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast" alt="">

* 本实验3种内存管理策略的内存利用率(与压測次数无关)
   - UDC策略:72.83%
   - Memcached策略:88.19%
   - Redis策略:98.77%

实验结论:
* Memcached的Slab Allocation机制在性能上的表现是最好的。且内存利用率也接近90%
* 当然。Memcached和Redis是基于进程堆的,主要用于缓存。UDC是基于Shm,用来做持久化存储。需考虑很多其它数据分布/同步/容灾等方面,3着不是必需用来直接比較,这里仅仅是单从内存管理策略上进行分析。

版权声明:本文博客原创文章,博客,未经同意,不得转载。

通用Key-Value存储系统的存储管理策略解析的更多相关文章

  1. Key/Value存储系统etcd的特性

    etcd 是一个高可用的Key/Value存储系统,和其他KV存储系统不同的是,它的灵感来自于 ZooKeeper 和 Doozer,主要用于分享配置和服务发现.利用 etcd 的特性,应用程序可以在 ...

  2. Redis 学习(一) —— 安装、通用key操作命令

    一.Redis介绍 1.介绍 通常,在系统中,我们会把数据交由数据库来存储,但传统的数据库增删查改的性能较差,且比较复杂.根据 80/20 法则,百分之八十的业务访问集中在百分之二十的数据上.是否可以 ...

  3. JVM内存模型及垃圾收集策略解析(一)

    JVM内存模型是Java的核心技术之一,之前51CTO曾为大家介绍过JVM分代垃圾回收策略的基础概念,现在很多编程语言都引入了类似Java JVM的内存模型和垃圾收集器的机制,下面我们将主要针对Jav ...

  4. redis中key的过期键删除策略

    Redis过期键删除策略 Redis key过期的方式有三种: 被动删除:当读/写一个已经过期的key时,会触发惰性删除策略,直接删除掉这个过期key 主动删除:由于惰性删除策略无法保证冷数据被及时删 ...

  5. <Redis> 入门二 五种数据类型的操作、通用key的操作、发布订阅

    文档参考:http://www.redis.net.cn/ string - > key value 简单的keyvalue,常规计数:例如微博数,粉丝数 set     -> key v ...

  6. 第六篇 ORACLE EBS用户界面通用元素或功能背后的道理解析

    本篇打算介绍一下ORACLE EBS用户界面(User Interface)中通用的元素或功能背后蕴含的一些道理.这些通用元素或功能包括: List of Values (LOV),值列表 Flexf ...

  7. JVM内存模型以及垃圾收集策略解析

    http://xmuzyq.iteye.com/blog/599750 一 JVM内存模型 1.1 Java栈 Java栈是与每一个线程关联的,JVM在创建每一个线程的时候,会分配一定的栈空间给线程. ...

  8. JVM内存模型及垃圾收集策略解析

    一 JVM内存模型 1.1 Java栈 Java栈是与每一个线程关联的,JVM在创建每一个线程的时候,会分配一定的栈空间给线程.它主要用来存储线程执行过程中的局部变量,方法的返回值,以及方法调用上下文 ...

  9. 【redis专题(8)】命令语法介绍之通用KEY

    select num 数据库选择 默认有16[0到15]个数据库,默认自动选择0号数据库 move key num 移动key到num服务器 del key [key ...] 删除给定的一个或多个 ...

随机推荐

  1. Android - 和其他APP交互 - 把用户带到其他app

    Android的重要功能之一就是app可以根据要执行的操作让用户启动另外一个app.例如,app有一个商业地址然后想要在地图上显示,并不需要在app中加一个显示地图的activity,可以直接用Int ...

  2. ATL 创COM物

    我原来以前写dll创建过程,而直接使用LoadLibrary加载动态库. 但ATL提出了一个非常重要的特点是引入COM对象的概念. 首先. ATL active template library该活动 ...

  3. ICT工作的思考&lt;两&gt;

    2周奋战.我负责的LB昨天完成了最后一个模块.最后20日. 一周早于预期,经理说,出乎他的意料.So 奖励表,昨日,管理人员与我们合作,吃烧烤补补身子.我只想说,最后一个喘息. 这两周的生活确挺忙碌的 ...

  4. 真实故事:网站遭遇DOS攻击

     网站遭遇DOS攻击 一个.事件背景 长假对于IT人员来说是个短暂的休整时期,可IT系统却一时也不能停.越是节假日,越可能出大问题,以下要讲述的就是一起遭受DOS攻击的案例. 春节长假刚过完,小李 ...

  5. Python 获取Twitter用户与Friends和Followers的关系(eg, 交集,差集)

    CODE: #!/usr/bin/python # -*- coding: utf-8 -*- ''' Created on 2014-7-30 @author: guaguastd @name: f ...

  6. 解决因特网和xshell考虑到问题

    首先需要解释.我们学校的网络是免费的.无论是实验室或宿舍.因此,互联网是基于Mac地址分配IP的,所以我VirtualBox安装了centos之后,话.就须要将VirtualBox的mac地址改成和我 ...

  7. iOS_21团购_发送请求【点评】数据

    结果表明,一个简单的请求: 用到的点评封装的类: 使用tableView简单展示: // // DealListController.m // 帅哥_团购 // // Created by beyon ...

  8. android做设计的每一个屏幕尺寸和分辨率(一个)

    一个.与分辨率无关 1.使用dp(dpi) Android密度不依赖像素(dp)指定屏幕尺寸,它同意不同的屏幕尺寸和像素密度类似设备通过缩放来达到同样的效果. (不解决不同屏幕尺寸的问题?) 2.的资 ...

  9. Git协作流程(转)

    Git 作为一个源码管理系统,不可避免涉及到多人协作. 协作必须有一个规范的流程,让大家有效地合作,使得项目井井有条地发展下去."协作流程"在英语里,叫做"workflo ...

  10. 当一个线程进入一个对象的一个synchronized方法后,其它线程是否可进入此对象的其它方法(转)

    对象的synchronized方法不能进入了,但它的其他非synchronized方法还是可以访问的 对每一个class只有一个thread可以执行synchronized static method ...