[转]SnowDB文件索引数据库系统

帖子来源这里http://perlchina.sun126.com/cgi-bin/ccb/topic_view.cgi?forum=6&article_id=0006050531225335&publishtime_id=0006050531225335&page=15&floor=0

该数据库实现代码在这里http://perlchina.sun126.com/noncgi/usr/affix/050604112053__6_snow.zip

更多讨论在这里http://perlchina.sun126.com/cgi-bin/ccb/forums_list.cgi?forum=6&class=&page=15


[转]SnowDB文件索引数据库系统


摘自 中国Perl协会 用户推广组 julius 2005-05-31 22:53
http://perlchina.sun126.com/cgi-bin/ccb/index.cgi


一,写在前面
废话少说,我很早就想搞一个这样的系统,问起来别人一直被回绝,但是我还是孤独的开发中
直到PerlChina的一次聚会,发现好多人也对文本数库感兴趣,因此来写一份我开发以来
的文档,给感兴趣的朋友
为什么叫SnowDB,因为是我在一个大雪天开始开发的,这个东西不能叫做文本数据库
他已经不是文本文件的概念,正确的说应该叫做文件索引数据库系统,着重点在索引的操作
而不对文本文件的操作

二,谈谈以往文本储存数据的弊端
以往文本储存数据一般都是以行为单位的"/n"结束的数据储存,这样的储存问题就很多
比如删除数据,如果在一个几万行数据库里面,你要删除掉前面的某行,对改行做删除标记
那么着行占用的空间无法释放,如果在次插入数据就来填充,就需要遍历一次行,效率很低
遍历操作也一样很没效率,比如你要遍历最后5行,那么就需要遍历前面所有行的数据来确定
最后行的位置.效率问题就很严重


(以上都是废话,你可以不看)

下面进入正题
一. SnowDB文件索引数据库,0.04版文件储存结构
文件划分为3个文件
1.结构定义文件
这个文件很简单,用"/t"分隔开的数据,每个数据表示列的的长度@column_length,把这些加一加就是行的长度(定长)
例子:$sdf="10"."/t"."4"."/t"."12" #表示第一列数据长度为10,第二列为4,第三列为12

2.索引文件(重点)
索引文件有点复杂.

索引文件有一个文件头,占用8字节长度,地址是000000-000008这里表示索引文件的索引区大小,在创建索引文件的时候创建
比如你创建一个能够容纳60000行记录的索引,索引头就是60000*4,创建有该索引文件最多只能索引60000个记录,在不够的时候
虽然可以扩充,但是扩充需要效率一定效率(不足前面补0)


索引头,索引头和索引文件头不一样,占用16字节,地址在000008-0000024这里16个字节划分为高8位,和低8位,高8位表示索引
区域内有效索引大小,低级8位表示空间回收区大小
例子:$indexhead=0000000000000000 #一个全新的索引头就是这样
在插入一行数据以后索引高8位+4
删除一行数据以后高8位-4,低8位+4
在插入数据时候检查到低8位不等于0(就是有可回收的空间)就低8位-4,高8位+4

索引区:索引区就是索引数据的
索引区存储的地址是每个在数据文件上存储行的开始地址,地址原始是8字节,经过打包hex2bin以后是4字节,之间没有分隔符
在插入一行数据以后,通过检索索引头的高8位找到索引区最后地址,然后插入被插入在数据文件中的行开始地址
在删除数据以后,读出被删除行下面所有的索引数据往前盖4字节,同时找到低8位+索引文件头找到文件最后的位置插入被删除行的地址
在插入数据时候检索到有空闲空间,就在空闲区读取得空闲地址,在数据表中插入,然后索引文件长度-4去删掉被利用的空闲地址

3.数据文件
数据文件基本没什么东西说
只有一条规则,根据索引文件得到的地址seek到地址,然后sysread,或者syswrite


二.寻址运算
1.索引文件头地址就是0 = seek(FILE,0)
2.索引文件索引头地址 = 索引文件头占用的8字节 = seek(FILE,8);
3.索引文件索引区开始地址=索引文件头占用的8字节 + 索引头占用的16字节 = seek(FILE,24);
4.索引文件索引区结束地址=索引文件头占用的8字节 + 索引头占用的16字节 + 索引头高8位数据 = seek(FILE,24+$index_header[0]);
5.索引文件回收区开始地址=索引文件头占用的8字节 + 索引头占用的16字节 + 索引文件头的数据 = seek(FILE,24+$index_head);
6.索引文件回收区结束地址=索引文件头占用的8字节 + 索引头占用的16字节 + 索引文件头的数据
+ 索引头低8位数据 = seek(FILE,24+$index_head+$index_header[1]);

7.数据文件行地址计算= 索引文件行索引bin2hex计算以后得到的地址 seek(FILE,bin2dex($address_raw))
然后read结构文件的数据和长度sysread(FILE,sum(@sdf));
8.数据文件列计算 = substr($data,0,$sdf[0]);substr($data,$sdf[0],$sdf[1]);substr($data,$sdf[0]+$sdf[1],$sdf[2]);

看上去复杂,其实很多东西可以复用,比如(索引文件头占用的8字节 + 索引头占用的16字节)很多地方都复用

今天先说到这里,明天要说的是_Insert,_delete,_update,select,这些伪操作


"二.寻址运算"
里面说了如何寻址,就知道了
碰见寻址里面的值回头看前面,在哪里得到!
索引区的长短,就在寻址里面

加/减记录是我现在要说的
首先说_insert
1.在没有可回收的空间的情况下,一个insert首先根据行结构填充数据,用寻址运算$_header[1]/4*$row_length得到地址,seek数据文件到地址

插入数据,然后seek索引文件到$_header[1]+24得到地址,插入pack后的4字节数据地址

2.在有可回收空间的情况下,一个insert首先根据行结构填充数据,用寻址运算得到$_header[0]+HEADER_SIZE+$_header[2]-4得当文件尾的倒退

4字节地址,取出最后4字节地址,seek数据文件到地址插入数据,减少索引文件4字节长度以删除用掉的空间,以后运算同上


然后说说delete
1.delete最简单不过了,对数据文件不变,由于索引定长4字节,那么找到删除数据的位置应该不难,读出要删除数据的4字节地址,然后把后面的索

引往后盖一个位置,不会消耗掉多少时间,20万行记录才700K左右,移动700K数据很快,盖好以后把去出来被删除数据的索引写的文件的末尾,当作

回收的空间


下面说说update
1.这个不用说了吧,找到地址,seek过去怎么弄都可以


下面重点说select
1.select是重点的重点,整个引擎的核心,除了insert以外其他操作都依赖select
由于多次回收空间以后数据文件里面将存在很多碎片段,读一个索引seek一个数据,这种方法不是我们提倡的,没效率
为了尽量减少IO,我们select的时候对碎片进行一次重组(现在还没有重组cache),当然是索引重组,不是数据重组
这样就可以读取连续数据段大大的减少IO次数,对于数据分解还是用老实的遍历

(具体可以看程序中注译)

未来的开发路线
对于引擎的开发不会中断,要一直保持下去,去吸纳最新的技术,什么分叉表,B-树,空间距阵
将来要发展的是cache,现在整个系统没有cache属于硬系统,效率直接关系到引擎
cache会分很多,比如重组cache,查询cache,操作cache,利用cache字节编码做到同时写文件,读文件
cache的效率会提高60%以上
现在Perl5做不了fork(),看Perl6的了,至少现在除了C语言其他语言fork()都还不行,问题很多
估计fork()以后,效率会提高3倍以上

seek FILE,HEAS_SIZE+index*4
read(FILE,$index,index*4);

操作中只用delete移动节点,select,inster,update都不移动索引节点,delete的使用率不会这么高吧,真的要频繁使用delete不要说索引db文件也会碎片分裂掉,你试试对mysql进行插入10个数据,然后删除掉8,在插入11个数据,删除掉9个,这样一来重复的操作,mysql查询效率直线下降

首先说inster操作,插入一个数据,首先写数据,然后写索引,最后写索引头,
1。写数据的时候突然断电,不要紧,因为索引没写上,这次操作就报废
2。写索引的时候突然断电,CR一下db文件判断数据是否写上,写上了就补充完索引
3。写索引头的时候突然断电,这个最麻烦,我是每每次操作1行以后马上写头版头,而且是覆盖的,所以破坏掉,最多最后一次操作报废

update不对操作索引,对db也是按照区块操作,最多损坏一行的中一个字段

select没有写操作就不说了

delete这个我头最疼,移动的节点最多,现在解决的方法有2个,一个稀梳索引,一个是短节点移动
稀梳索引目前问题还太多,
现在用的是短节点移动,节点是一个一个搬运上去的,不是下全部写回去,短节点移动也只能损坏一个行,数据


所以我说索引整个报废的可能性为0,
我的操作都是继承来的,从文件头开始继承,到索引头,到索引,到数据
操作是按寻址,文件头不可写不会坏掉,除非磁盘坏了


文件引擎->cache管理->解析SQL
|______碎片引擎->___|

现在我弄的只是文件引擎部分,
cache管理会是我是一个重点,我把状态划分为bus和free两种状态,bus的时候阻挡最后一条链路存到cache等待下一次请求来临
而任何read都可以通过碎片引擎把文件和cache重组

我把多文件和大单一文件结合,
当数据库运载的时候,负载0的时候不阻断因此只有单一数据文件
如果负载繁忙的时候,就产生很多碎片文件分布负载,
这样既有单文件的易用也有多文件的负载能力

别老钻到多文件里面,你也想想ext2是链表,ntfs是散列,能支撑的了这么多文件吗?
以前的bbs经常跨在文件系统崩溃上,而且多文件备份也不容易
嘛辣和我说他现在最头疼的就是贴子备份


1.文件的储存方式是什么?是否压缩?使用什么编码?
2.你的设计重点是索引,不过你的索引和文件是紧耦合的,你如果想改变其中的一个都要对另一个有影响.如何解决?
3.对于一个应用来说,要求你提供的接口是稳定的,你想提供什么样的接口(SQL)?

回复:
1.文件是不压缩的,因为动态数据库追求的是速度, 全部按照计算你的程序编码,你 use UTF-8的时候,就是UTF-8编码?

2.这种情况只有发生在树状索引上,不过现在树状索引还没有被使用,只是测试版中存在
在线性索引中,索引就好象指针一样对着目标,对目标操作只有一种,就是改变其内容,不能改变目标的位置
要改变位置只要移动索引就可以.

3.这个是老大难的问题,一种"弱SQL",是目前的接口,就是最基本的select,where,limit,insert,delete,update的实现操作
也就是说sql优化需要你自己做,程序不能给你提供sql parse




附:数据库存储结构示意图

你可能感兴趣的:(数据库)