内存新算法

来源:互联网 发布:qq透明皮肤软件 编辑:程序博客网 时间:2024/06/06 09:29
Key-Value System的空间管理
2011-05-06 21:10

        今天给大家讲的是项目中的《空间管理》这个模块的内容,记录一下,备忘。

        这幅图是我们项目的框架:Interface是对外的接口,Index模块是个hash_tree(三次hash保证唯一性),今天我讲的是Disk Space Manager这个模块。

        所谓空间管理,他的目的就是要权衡“空间利用率”和“运行效率”这个问题。“空间利用率”和“运行效率”是一对矛盾体,想要空间利用率高,就会受到运行效率的限制;想要运行效率,空间可能就会产生“碎片”。何为碎片,碎片分为内碎片和外碎片:

        1.外碎片:分配给程序的存储空间没有用完,有一部分是闲着的,这部分空间就是内碎片。

        2.内碎片:不能满足程序要求的小的可以分配的存储空间是外碎片。

         KVS空间管理算法的发展过程如下:

         0.buddy system

         1.位压缩的buddy system(用bit-map压缩减少内存)

         2.持久化的buddy system

         3.优化的buddy system

         4.基于buddy system的KVS空间管理算法(我们目前系统版本用的是这个)

         5.改进的KVS空间管理算法(以后将要做的)

          KVS空间管理算法最开始是基于linux中的buddy system的算法发展来的,关于buddy system的算法非常简单,这里不说,网上有相关资料,linux内核书和蓝色皮的数据结构也有提到。位压缩的buddy system是当时暂时没有好的处理办法,就用位压缩处理了一下。另一方面,这个系统要隔一段时间把内存中的数据复制到磁盘中进行备份,遇到掉电等情况,可以恢复数据。但是这个时候因为程序里有很多变量都是存的内存地址,这样备份的时候要是把内存地址备份,等恢复数据的时候,原来那块内存可能已经被使用了。我的办法是把内存地址减去内存的首地址,这样管理的区间ID就是从0开始的数字,等恢复的时候把这个数字再加上内存的首地址就可以对应到相应的内存块。此时,这个空间管理(空间大小是1TB的磁盘)使用了1GB的内存,用位压缩也是到了700MB,不可忍受。

         网上找到一篇论文,题目是:一种改进的伙伴系统内存管理方法,作者:郑晓曦,张虎。对我有些启发。

         buddy system他可以很好的解决外碎片问题,但是他没有解决内碎片。linux本身也是用了buddy systemslab分配来解决的,buddy system只负责处理外碎片。这个论文就巧妙的改变buddy system的结构,让改进过的buddy system能够同时处理外碎片和内碎片问题。现在来进行一下对比:

         buddy system的size大小是2的k次幂:1、2、4、8、16、32……这样导致的问题是每次切割不能够按照用户的大小需求进行切割,如果按照用户想要多大空间就切割多大,那么剩下的块可能就不能够插入到可用空间表中了。例如,在可用空间表中找到了8大小的存储块,用户申请5这么大,要是把8切割成5和3,3就插入不到可用空间表中了。buddy system让存储块是2的k次幂是为了找伙伴非常快速,所以受到了限制。

         解决这个问题的办法是:把可用空间表size后面的单链表的范围由原来的确定值,现在变成一个范围,size是2^i后面的链表挂的存储的是在(2^i,  2^(i+1)]这个区间。这样就可以每次切割的时候就可以按照用户想要的大小”恰好“的切割,并且只用切割一次。那么合并的时候怎么办呢?原来通过2的k次幂这个结构可以快速找到buddy来合并。现在的做法是增加物理块链将所有块(空闲块和使用块)按照物理地址的顺序组织成一个双链表。每次合并连续的向前,向后合并相邻物理地址的空闲块。

         基于以上启发,回过头来看项目的数据规模:单机存储1000万条记录,平均大小100K,单机存储1TB,单条最大长度5Mb。最初的buddy system管理1Tb空间,每16K作为大小为1的内存块需要大概6.7*10^7这么大可用空间表范围,单条最大长度5Mb,5Mb / 16 kB = 320,也就是320到6.7*10^7这段区域是从来不会申请的。于是,有了目前的KVS空间管理算法:

所有大于320的存储块都放在321的链表里。现在可用空间表size也是连续的,并且范围不大。每个存储块的结构体如下:


ptr_disk返回可用空间的首地址

flist_preflist_suc可用表的左右指针

node_prenode_suc独立块左右相邻的物理地址映射

size 申请的连续空间的大小

used块空闲标记

磁盘地址、内存地址、node_id相互转化关系是:

 

每次用户alloc一个空间,返回给用户的是磁盘的地址,那么free的参数也应该是磁盘的地址,上图解释的是如何通过磁盘地址找到内存地址来释放相应的内存块。

有个非常巧妙的地方:当块空闲的时候used标记为0,flist_pre,flist_suc这两个指针是可用表后面的链表的指针。当存储块不空闲的时候,used标记为1,此时flist_pre,flist_suc这两个指针是没有用的,于是把这两个指针复用成hash_tree里树的两个左右指针。

目前这个算法占用内存267MB左右。

下一步的改进将会从“平均大小100K”入手,可用表后面的存储块将会在100附近达到峰值,什么策略会根据这个数据特点,让效率更高呢?敬请期待改进的KVS空间管理算法~


buddy system算法是好,但是他不是通用的算法。我们根据数据规模特点,找到了一种改进的办法要好于buddy system,通过这个算法的发展过程我们可以得出一个结论:专用的算法会比通用的算法更出色,重要的是挖掘数据本身的特点!


原创粉丝点击