20140517济南源创会技术演讲总结

前言:

此总结完全基于昨天我的演讲PPT来写的,请先点击下载PPT再阅读本文。演讲前一天晚上,被红薯大哥热情感动拉着喝了点酒,导致第二天下午演讲,头还有点痛,结果演讲的那么语无伦次,请大家不要再扔蛋了。我个人很高兴能有这么多人来参与,这是我本人在济南圈里面连续6个月做的第6次演讲了,时间过去半年了,感慨下时间过的真快啊!

我写的总结主要是基于存储的,内容比较枯燥,这些都是我看MapDB代码总结出来的,并不像部分朋友说的仅仅是个理论完全可以下载MapDB代码进行对照如果你对我写的这个大篇总结实在没兴趣,那么就看看文章最后的配图吧,会心一笑,就此别过。如果感觉文章很给力,毕竟也是敲了那么多字,劳驾请给此文点个赞,方便我去 @红薯  大哥那里领赏钱烤地瓜吃,O(∩_∩)O~。

基于PPT内容的总结正文:

第一页:声明了今天的演讲标题“MapDB原理与应用”,同时欢迎感谢开源中国的到来,并介绍了下本人 @和风赛跑  ,更加祝愿济南圈的活动能继续蓬勃地在开源中国的支持下发展下去。

第二页:简单列了下今天的演讲提纲,不重复了

第三页:介绍了下MapDB以及作者Jan,捷克人,全职工作在MapDB上,MapDB是JDBM改名后的项目,JDBM开始累积大概有十二年的开发历史。在作者描述MapDB的时候,有这么句话“innovative database engine”,我比较看重第一个单词“innovative”,意为“创新”,就是不一般,挺二的吧;之前对JDBM的代码以及思路并不陌生,改名后突然冒出来这个单词,我感觉很奇怪,对它很有兴趣,加上我本人又比较较真,

所以就起了把它掀个底朝天的杀机,后续整个的演讲我也在强调,它的不一般的“二”在哪!

第四页:上页说过MapDB是个键值存储,Map代表了键值,DB代表了存储;对key-value结构的存储并不是陌生的了,

接下来谈下这个东西的架构设计;首先要明白存储的模型是哪条道上的,是存放到磁盘文件的,还是基于内存的,然后再选择相关的存放方式;

存放文件模型,存放有两种方式:文件管道和直接内存映射(64位OS上),内存(序列化)存放模型有:jVM内存内和非JVM内存内,此外还有一种内存模型(非序列化),比较简单。基于这种模型之上又有了额外的需求定制,是否支持事务,是否支持异步写,是否支持缓存,等等。

这些额外支持的功能等于对存储模型做了包装,跟单个模型之间都可以互相组合

第五页:用图阐明了第四页表达的意思。

第六页:谈谈存储,存放数据到数据文件中后,我们总得要知道我的数据在哪吧,比如你告诉我在文件里面的第101行,往后30行是我的,那么我就去茫茫文字里面去找到第101行,然后后续读出30行内容来就是我想要的。

该页表达了存放数据之后,至少得返回两个值:数据存放位置和数据的偏移量(长度)

第七页:知道了这两个值后,就要面临着怎么把这两个值存放起来的问题,实际上我们在存储的时候基本都是采用分页的存储,如果数据很大的时候就会牵扯到要跨页存储的情况,一旦跨页,那么就要分多次去寻页读取,

所以除了位置和偏移量之外还要个有布尔类型(0,1标识即可),合计共三个数字变量,不要忘记直接存放此三个值占用空间还是比较浪费的,所以我们就要把此三个数字合并到一起变成一个数字,作者采用了位运算的方式把此三个变量合并到一个数字里面,合并后的数字可以再通过位运算逆向计算得到那三个值,存放此一个数字在空间上是要比存放三个数字节省不少空间的哦(寸土寸金)。

现在得到了这个数字,我们就要把他存放在索引文件中,索引文件中按照顺序存放一个记录号(Long)+一个我们位运算后的数字(Long),可以看到这么一对(2个Long),合计就是存放大小固定的16Byte空间,那么如果知道记录号是28,那么就去(28-1)*16的位置上读出一个偏移量为8的数据就是该记录号,后续读出一个偏移量为8的数据就是那个我们把三位数字位运算后合并后变成的那个数字了。接下来的思路都是围绕记录号和怎样在数据文件里面存放数据展开围剿。

第八页:话题走到了怎样存放数据到数据文件,顺序存储还是分页存储,基于顺序存储的问题,我在今年一月份做过的技术分享中剖析过,然后对比了分页存储,如果参加了我那次的分享会,此页谈的内容就很明白了。

还是啰嗦下顺序存储(一月份分享内容部分)吧,一个文件里面把主要区域划分为索引区域A,数据区域B,索引区域里面记录了记录号+数据位置+数据的偏移量,

那么索引区域增加空间,就是把数据区域的第一行数据读出来释放出来的空间给索引空间,同时把该记录追加到数据区域的最后一行,并更新此数据在索引区里面的索引记录。

数据区域里面删除记录的时候,删除记录位于数据区域的最后一条就直接抖动文件,如果其位于数据区域的中间,

就把把该数据的释放空间分配给此记录的紧相邻的前一条数据记录占用空间上,

如果其是数据区域的第一条记录,那么就把数据区域的第二条向前迁移数据位置变成区域第一条记录,占用空间等于原来第一条记录空间+原来记录空间,此过程不影响其他数据记录。

关于分页,大学课堂时候就学过其思路,在此不再啰嗦

第九页:分页,一般都是给页面设定一个固定大小,那么存储的时候,数据记录可能会溢出跨多页,也可能会在单页内,

由于删除更新操作,会遗留下很多零散的空白区,成为碎片。

第十页,第十一页,主要是在陈述分页引起的碎片问题,碎片多少,要看页面设定大小跟实际的数据特征之间的匹配情况,

同时,当我们有新数据申请空间的时候,那么是不是要完全考虑不惜多次I/O代价来重复利用这些碎片

第十二页:对碎片的重复利用是要先花代价对其管理记录下来,否则就不好去寻找碎片和复用碎片空间。

讲到此时,我强调了一句,这些分页存储思路以及分页带来的问题,这些问题大家都不算陌生了,在老版本JDBM里面就是采用的此套思路,相比顺序存储是改进了很多,

但是如果冠以“innovative”修饰,我感觉就有点牵强了,属实的话作为一名一向严肃的程序员,肯定是坐不住去声讨两句的,会强烈要求去掉“innovative”!

第十三页:“innovative”出现了点苗头,介绍了页面大小动态分配,以页面大小为10起步,步长为10进行递增,10,20,30......80,90,100,有这10种大小不一样的页面,10这个数字是我为了好理解取的一个样例数字,并不是在存储中用到的真实数据

第十四页:谈到了怎么分配这些大小不一的页面,数据大小100以下的记录就是除以10后得到的值,如果余数不为零那么就再加1,然后此值乘以10就是需要的合适页面。

比如52,就需要分配60的页面;48需要大小为50的页面;70需要大小为70的页面;

第十五页:主要演示在页面大小动态的情况下,怎样考虑分裂大页面(不一定是那个最大值的页面),

比如现在数据大小为32,更新为数据大小为48,那么就会先释放大小为40的页面然后申请一个大小为50的页面,

如果数据空间里面没有50的页面,假设依次大小为60,70,80的页面,都没有空闲的,那么就会再去找大小为90的页面,

现在设定大小为90的空闲页面存在,但是不能把此页面直接分配给那个大小数据记录大小为48的页面吧,毕竟50大小的页面就可以搞定48,太浪费!所以我们需要考虑把90的页面分出一个大小为50的页面,剩下的大小为40的页面会标记为空闲;如果90,100的页面都不存在,那么直接直接就在磁盘文件里面多划出一份符合50的页面出来。

如果数据大小为48的记录更新为大小为22的记录,同时大小为30和大小为40的页面都不存在,会什么情况呢?

首先它会先释放大小为50的页面,然后没找到30和40的页面后,就会分裂大小为50的页面(假设此页面没被别的线程抢去),

分裂结果为30页面+20页面,30页面分给新记录(数据大小22),20页面标记为空闲。

除了分裂,也要强调更新记录不需要重新分配页的情况,比如数据大小48更新数据大小为42的记录,

这都是在页面大小为50的页面中进行,不需要再重新申请。

第十六页:讲述页面溢出问题,每个数据的记录很容易会超过最大页面值得大小;

如果数据大小超过页面最大值100,那么就先得到除100的除数,然后再把不够100的数字进行分配合适的页面,

比如138会先分配大小为100的页面,然后再考虑分配38,肯定就是大小为40的页面了,

最后合计就是数据大小为138的记录会分配一份大小为100的页面+一份大小为40的页面,

有个注意点就是,它一定不会分为90页面+50页面或80+60这样情况的页面,

如果一个数据记录长度大小溢出最大页,一定先按照最大页来分配,

即使没有空闲的最大页面,重新划出生成一份最大值得页面也绝不会考虑去占用两个小的页面,虽然他们的总和大小也都会合适。

第十七页:如果页面大小固定,比如为X,那么只要有数据申请新页面,那么肯定就是申请大小为X的页面,

但是现在我们的页面大小不固定,会有十种大小类型的页面申请。

问题会出现如下三个:

1,每种页面大小的类型释放后怎么放,怎么记录

2,怎样应对各种页面大小类型的申请

3,怎么分配页面,比如现在有两个大小为30的页面A和B都释放了页面,再新申请大小30的页面时候,

是分配给它A页面还是B页面呢,还是说这种分配策略是随机分配

第十八页:介绍作者专门开辟的来存放数字的数据结构,

该数据结构有多个页面组成,此页面大小是固定的,跟之前说的那个动态大小的页面不是一回事,不要搞混了。

每一页的页面空间大小固定,里面除了两个头空间外,其他都是存放数字的。

只不过这里存放Long类型的数字,作者采用了6Byte的空间进行存放。

头空间的第一个值是存放该页面的占用空间大小,第二个值是存放指向下一个页面的指针,其实就是下一个页面区域的位置,每个这种页面的偏移量都是固定的常数,不用再保存。

在索引文件里面需要专门分配空间存放指向第一个页面的位置偏移量,比如:

页面A,B,C,那么就是索引文件区域里面会存放页面A中的第二个Long空间的偏移量与页面A中的里面除了两个头空间区域外的非空Long数字的偏移量做位运算的结果,这样通过索引空间里面的值计算后很容易得到页面A中的非空Long数字;

继续,索引区域指向页面A的最后一个非空数字,页面A的第二个头区域指向页面B的偏移量,页面B的第二个头区域指向页面C的偏移量,因为页面C是最后一个页面,那么页面C中的第二个头区域指向零即可。假设这时候又新分配一个页面A1用来存放数字X,那么页面A1会放到页面A的前面,同时让页面A1中的头文件区域第二个值指向页面A,索引区域就指向了页面A1中的数字X,再来一个数字Y,那么数字Y是放在X的后面,同时更新该页面的前页面指向该页面中的指针里面的非空Longs数字的位置位,在取出X后,会更新索引区域指向Y,接下来再输出的话就是输出Y。

总结,页面之间是后进先出的,页面内部的多个数字之间也是后进先出

看到有朋友留言问我为什么页面之间是后进先出,而不是采用先进先出,

如果是页面之间是先进先出,那么再弹出一个页面后需要遍历得到它的页面来更新它的指向下个页面的数字为零,

遍历得到其上个页面是比较费时的,尤其在它的长度变巨大后

第十九页:利用上页讲解的数据结构进行存放各种大小不一样的空闲页面

第二十页:利用第十八页讲解的数据结构进行存放索引文件中的非空数据记录使用的记录号

第二十一页:概述了查询数据记录,添加数据记录,修改数据记录,删除数据记录的各个操作、

查询数据记录:

拿到记录号后从索引中获取其位置、偏移量以及是否跨页溢出(其它省略,主要是这三个),根据此三个值就可以从数据文件中获取到数据。添加数据记录:

先从第二十页讲解的数据结构存储中获取记录号,

然后根据要添加的数据大小,从第十九页讲解的数据结构存储中获取需要的一个或多个空闲页面,

可能会有第十五页中讲解的页面分裂,最后数据写到空闲页面中。

修改数据记录:

修改数据记录的时候,已经拿到了记录号,不需要再重新申请;

先比较新记录大小是否跟旧记录大小在同一个页面内,是的话就不需要重新申请新页面。

然后根据新记录大小,去从第十九页讲解的数据结构存储中获取需要的一个或多个空闲页面,

可能会有第十五页中讲解的页面分裂,

删除数据记录:

归还记录号到第二十页讲解的数据结构存储中;

归还占用的一个或多个页面到第十九页讲解的数据结构存储中

第二十二页

之前例子是以页面之间以10为递增进行演示,而实际mapdb是以共有4096个页面,最小页面是16Byte,每个页面以16Byte递增,最大记录大小为64KB。

这种思路里面就是保证了每个数据记录至少占用一个页面,如果页面里面还有空闲空间的话,不再考虑复用。

这就是拿浪费空间换取不再多浪费磁盘I/O去复用那些碎片,算不算得上是以空间算时间呢。

第二十三页

MapDB中的DB讲解完了,现在开始讲解下Map了,Map就是提供用户输入Key-Value的输入口,

作者提供了两种数据结构类型,基于B-Linked-Tree实现的Map,另外一个是基于Hash-Tree实现的Map

第二十四页

讲解B-Linked-Tree,主要是它为什么能更好地解决并发,在节点分裂的时候不用额外加锁防止限制查询操作

伯克利数据库以及开源的Postgresql数据库采用了此算法建立索引

第二十五页

对B-Linked-Tree的特点进行了总结,不再重复,看PPT内容即可

第二十六页

对HTree算法进行了讲解,文件系统Ext3、Ext4好像都是采用了此算法。有一个长度为16的数组,里面都存放了一个16*8的二维数组A1,A1数组里面的每个元素也可以存放一个16*8的二维数组A2,A2数组里面的每个元素也可以存放一个16*8的二维数组A3,A1、A2、A3这是三个级别的数组,作者规定了最大就是有三级;如果hash值冲突,那么A3里面的发生冲突的子元素里面就会存放一个链表,链表里面的每个节点会存放一个key和一个value;假设没hash冲突情况发生,并且三级都全部展开,就是16*16*8*16*8*16*8=33554432,这是三千多万,再假设每个链表的长度是几十个,很容易放满十亿个数据,作者认为十亿数据还是适合用此数据结构进行存储的,但是如果数据量再巨大,那么发生hash冲突的情况会更多,导致会出现长度比较大的链表,就会性能下降

第二十七页

对两种tree进行对比,BTreeMap的每个节点存放若干个Key,若干个Value,其中Value可以放节点外,

适合较小值得Key;HTreeMap的每个链接节点存放一个Key+一个Value,适合较大值得key;

第二十八页讲解了下其它特性

第二十九页讲解了应用场景

第三十页:讨论,结束,谢谢大家参与

2014-05-18,一名致力分享原理分享思路的程序员 --和风赛跑 (John)


  

230059_IYej_1415990.jpg

230059_WImL_1415990.jpg

230100_nh66_1415990.jpg

230100_AKJb_1415990.jpg

230100_zfSK_1415990.jpg

230100_5cmn_1415990.jpg

230101_IOMb_1415990.jpg

230101_phaG_1415990.jpg

230101_PpST_1415990.jpg

230102_OAzl_1415990.jpg

230102_2Fvh_1415990.jpg

230103_5Y6M_1415990.jpg

230103_mA5p_1415990.jpg

附加上妹子吧,否则太对不起你把滚动条拉到这个地方了,O(∩_∩)O

112924_M56B_1415990.jpg


转载于:https://my.oschina.net/iamjohnli/blog/266267

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值