|
马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。
您需要 登录 才可以下载或查看,没有帐号?立即注册
x
基础这个东西是个比较笼统的概念,如果你之前学习过c语言, c语言被认为是数据|数据库 一 文本数据库类方针
文本数据库设计的方针,是完成文本等数据的组织和存取,屏障数据寄存的详细细节,向用户供应一个复杂便利的文本数据的拔出,修正,删除,查询的接口。
二 文本数据库思绪
咱们盘算将相干的文本数据寄存在统一个文件中,并以纪录为单元完成对文本数据的组织与办理。纪录可分为两中:定长纪录和不定找纪录。关于定长纪录,咱们可以用按次寄存的体例组织纪录,如许关于第N个纪录,咱们可以直接以N*RceLength定位它在文件中的寄存地位,关于读取,拔出,更新操作来讲,按次存入的组织体例会使它们的完成变得十分复杂且快捷;但是在删除纪录的时分,费事就来了,要持续坚持纪录在数据库中的按次寄存,在实行删除操作后,咱们还必需将前面的一切纪录向前挪动一个纪录单元,在纪录个数绝对少而且纪录长度绝对小的情形下,这类前移数据所支付的价值仍是可以承受的,当纪录个数变的良多,或纪录长度大的情形下,一个纪录的删除常常会陪伴着大批数据的挪动,这类价值却变的十分高。
以定长纪录的体例存入数据使得良多操作变的十分便捷,但是除下面所讲的数据删除晦气的要素外,定长纪录还有一个十分分明的缺乏,那就是在纪录的犬牙交错的情形下,以定长的体例寄存纪录能够会华侈良多的空间,由于咱们必需以一切纪录中最大的谁人纪录长度为一切纪录的长度,用如许的纪录空间寄存短的数据不免有点大才小用。处理这个成绩,咱们很快就会想到不定找纪录的体例,就是答应文件中分歧的纪录占有需要的空间而不用利用一致的长度。不定长纪录的体例完善的处理了空间华侈的成绩,但是却得到了定长纪录能疾速定位的特征,使得对数据的各类操作变的非分特别的费事乃至不成能。
咱们必需设计一种奇妙体例要组织数据,使得它不但能像定长体例那样疾速地定位数据库的纪录,又能像不定长体例那样节俭数据空间,尽可能削减数据删除所支付的价值。而要具有如许的特征,必定是这两种体例的联合。咱们用一个文件以不定长的体例寄存实践数据,处理了空间华侈成绩;别的咱们还设立一个帮助的文件,用于纪录这些数据的定位信息,定位信息以定长的体例组织与寄存,可以处理纪录的疾速定位成绩。为了前面会商的便利,咱们无妨给这两个文件命个名,寄存实践数据的文件咱们称它为数据文件(dbf),存入定位信息的文件咱们称之为索引文件(indx)。
咱们来剖析一下这类组织体例的详细完成。拔出数据时,咱们依据存入数据的巨细从数据文件(dbf)平分配一个适合的可用空间,并将数据存入此处,然后在索引文件(indx)添加一个新纪录用于寄存实践数据在数据文件(dbf)中的定位信息,如是一个新的纪录就如许子被拔出到数据库中了。读取纪录时,咱们先从索引文件(indx)中获得实践数据的定位信息,然后依据这些定位信息到dbf中定位并读取纪录的实践内容,由于定位信息是定长体例寄存,它可以十分直接地从indx中获得。删除数据的时分,咱们只需复杂地从索引文件中删除对应的定位纪录就能够到达目标,而无需对数据文件停止任何操作,定位纪录从索引中删除后,此纪录固然在数据文件中仍然存在却永久都不会再被会见到而被闲置。为了更无效的使用空间,咱们在停止删除操作的时分其实不只作如斯复杂的处置,咱们还设立一个额定文件,用于纪录数据文件中这些因删除而被闲置的空间,以便在适合的时分,使得这些闲置空间被再度寄存实践数据。此文件也以的体例纪录闲置空间信息,咱们称这个文件为闲置空间文件(left)。更新数据也绝对庞杂,假如新的数据长度小于原数据的长度,咱们可以复杂地将新数据存本来的地位,这类情形下咱们只需对数据文件(dbf)停止更新操作。而当新数据所需空间大于原始空间的时分,咱们只能保持原空间而另寻中央寄存数据,这里除对dbf停止写操作外,还得更新纪录在indx中的定位信息,并在left文件中添加被保持的空间信息.
三 文本数据库的精密设计与算法
下面复杂地剖析了文本数据库的完成办法。上面依据我的实例来引见文本数据库的精密设计与详细算法。
起首,咱们简地引见下面提到的三个文件的数据布局。
关于数据文件dbf,纪录是不定长并且是无序寄存(为何是无序,上面会有引见)的,不需求太多的申明。
索引文件indx以定长而且是按次的体例寄存纪录,也就说每一个纪录的长度分歧为RcdLength,并且第N个纪录寄存在文件中的N*RcdLength处.每一个纪录的格局以下:
纪录编号(ID)|偏移地位(LOC)|数据长度(Length)
个中,ID是数据库每一个呈现过的纪录的独一编号,分歧的纪录以此来独一标识本人,相当于数据库的健值。ID编号由体系递增分派;LOC纪录实践数据在数据文件dbf中存入的首地位,length则暗示此数据在dbf中所占用的空间。值得提出的是,ID编号N的纪录其实不必定是数据库中的第N个纪录,ID用以标识纪录的成分,而第N个纪录的N则是指纪录在数据库中的物理(对应于indx)与逻辑地位(对应于dbf);还有,length纪录的是该数据所占用的空间巨细而并必定同等于数据的实践长度(为何?).
ID,LOC,Length均为无符号整数,所以indx中每一个纪录的占用4*3=12个字节的空间,这很利于纪录的按次寄存和删除。
咱们还商定,indx文件中第一个纪录并非用于纪录数据的定位信息,而用于纪录全部数据库的相干信息,个中ID用于存入已被分
配的最大ID编号,LOC用于纪录数据库中的实践纪录个数,length用于纪录数据文件dbf的末尾地位。可以看出,ID值是递增的,在没有
停止任何删除操作之前,ID值与LOC永久相等,如删除操作,LOC必定小于ID值。别的,length值在良多情形下其实不与数据文件bdf的大
小相等,如咱们在dbf的末尾为一个新纪录分派了100个字节的空间而写入的实践数据只要90,谁人length值要比dbf的巨细大10.
left文件的纪录布局与组织体例和indx相似却更加复杂,它只要两个字段:
偏移地位(LOC)|空间长度(Length)
个中,LOC暗示闲置空间在dbf中的肇端地位,Length暗示此闲置空间的长度。一样,left文件中的第一个纪录不必来纪录闲置空间
信息,而是纪录本人的相干信息,第一个纪录的LOC值或Length用于纪录left文件中的闲置空间条数也就是本文件和实践纪录个数。
数据布局已陈说完整,上面咱们依据代码来谈谈实践算法:
<?
class TxtDB //文本数据库类
{
var $name=''; //文本数据库名
var $path=''; //数据库途径
var $isError; //失足代码
var $dbh; //数据文件dbf指针
var $indxh; //索引文件indx指针
var $lfth; //闲置空间文件left指针
var $lckh;
var $rcdCnt=0;//数据库的纪录个数
var $maxID=0; //数据库已分派的最大ID编号
var $leftCnt=0;//闲置空间个数
var $DBend=0; //DBF文件末尾指针
/*初始化函数*/
function TxtDB($name,$path='dbm')
{
$this->name=$name;
$this->path=$path.'/'.$name;
$this->isError=0;
$path=$this->path;
if ($name!='')
{
@mkdir($this->path,0777);//创立数据库目次
//创立或翻开数据库文件
if (!file_exists($path.'/'.$name.'.tdb')) $this->dbh=fopen($this->path.'/'.$name.'.tdb','w+');
else $this->dbh=fopen($path.'/'.$name.'.tdb','r+');
if (!file_exists($path.'/'.$name.'.indx')) $this->indxh=fopen($this->path.'/'.$name.'.indx','w+');
else $this->indxh=fopen($path.'/'.$name.'.indx','r+');
if (!file_exists($path.'/'.$name.'.lft')) $this->lfth=fopen($this->path.'/'.$name.'.lft','w+');
else $this->lfth=fopen($this->path.'/'.$name.'.lft','r+');
//为包管数据库操作的原子性,对数据停止加锁回护
$this->lckh=fopen($this->path.'/'.$name.'.lck','w');
flock($this->lckh,2);
fwrite($this->lckh,'lck');//壅塞其它并发历程对数据库的并行操作
//获得数据库的相干信息
$rcd=$this->getRcd(0);//从indx文件中读取首个纪录
$this->rcdCnt=$rcd[id];
$this->maxID=$rcd[loc];
$this->DBend=$rcd[len];
$rcd=$this->getLeft(0);//从left文件中读取首个纪录
$this->leftCnt=$rcd[loc];
}
else $this->isError=1;
}
/*设置indx的定位信息*/
function setRcd($rid,$id,$loc,$len)
{
fseek($this->indxh,$rid*12);
//挪动文件指针至纪录处
$str=pack('III',$id,$loc,$len);
//将整数紧缩到字符串中
fwrite($this->indxh,$str,12);
//将定定位信息 ID|LOC|Len 写入indx的第rid个纪录
}
/*获得定位信息*/
function getRcd($rid)
{
fseek($this->indxh,$rid*12);
//移至纪录处
$str=fread($this->indxh,12);
//记着纪录
$rcd=array();
//将紧缩的字符串复原为整数
$rcd[id]=str2int($str);
$rcd[loc]=str2int(substr($str,4,4));
$rcd[len]=str2int(substr($str,8,4));
return $rcd;//前往第rid个纪录的定位信息
}
/*设置闲置空间纪录*/
function setLeft($lid,$loc,$len)
{
fseek($this->lfth,$lid*8);
$str=pack('II',$loc,$len);
fwrite($this->lfth,$str,8);
}
/*记着第lid个闲置空间信息*/
function getLeft($lid)
{
fseek($this->lfth,$lid*8);
$str=fread($this->lfth,8);
$rcd[loc]=str2int($str);
$rcd[len]=str2int(substr($str,4,4));
return $rcd;
}
/*停止数据库操作并释放数据加锁*/
function close()
{
@fclose($this->dbh);
@fclose($this->indxh);
@fclose($this->lfth);
@fclose($this->lckh);
}
/*从闲置空间中寻觅一个巨细起码为len的空间
利用最好合用法 */
function seekSpace($len)
{
$res=array('loc'=>0,'len'=>0);
if ($this->leftCnt<1) return $res;
//没有闲置空间
$find=0;
$min=1000000;
//遍历一切闲置空间信息
for ($i=$this->leftCnt;$i>0;$i--)
{
$res=$this->getLeft($i);
//找寻到巨细恰好适合的空间
if ($res[len]==$len) {$find=$i;break;}
//找到可用的闲置空间
else if($res[len]>$len)
{
//力争找到一个最适合的空间
if ($res[len]-$len<$min)
{
$min=$res[len]-$len;
$find=$i;
}
}
}
if ($find)
{
//找到了适合的闲置空间
//读取闲置空间信息
$res=$this->getLeft($find);
//用left文件删除此闲置空间的纪录信息
fseek($this->lfth,($find+1)*8);
$str=fread($this->lfth,($this->leftCnt-$find)*8);
fseek($this->lfth,$find*8);
fwrite($this->lfth,$str);
//更新闲置空间纪录数
$this->leftCnt--;
$this->setLeft(0,$this->leftCnt,0);
//前往取得的闲置空间了局
return $res;
}
else //掉败前往
{
$res[len]=0;
return $res;
}
}
/*拔出纪录至数据库content为纪录内容,len限制纪录的长度*/
function insert($content,$len=0)
{
$res=array('loc'=>0);
//纪录长度没有指定章依据数据实践长度指定
if (!$len) $len=strlen($content);
//试图从闲置空间中获得一块可用的空间
if ($this->leftCnt) $res=$this->seekSpace($len);
if (!$res[len])
{
//没有找到可用的闲置空间则从数据文件末尾分派空间
$res[loc]=$this->DBend;
$res[len]=$len;
}
//更新数据文件末尾指针
if ($res[loc]+$res[len]>$this->DBend) $this->DBend=$res[loc]+$res[len];
$this->maxID++;//更新最大ID编号
$this->rcdCnt++;//更新数据库纪录个数
//将更新永世写入数据库
$this->setRcd(0,$this->rcdCnt,$this->maxID,$this->DBend);
$this->setRcd($this->rcdCnt,$this->maxID,$res[loc],$res[len]);
//将实践数据写入从dbf分派的空间处
fseek($this->dbh,$res[loc]);
fwrite($this->dbh,$content,$len);
//胜利前往新纪录的编号
return $this->maxID;
}
/*寻觅编号为ID的纪录在数据库中的地位编号N*/
/*由于ID编号在indx中升序分列可以使用二分查找大大进步查询速度*/
function findByID($id)
{
//数据库中没有纪录或编号超越以后最大ID编号
if ($id<1 or $id>$this->maxID or $this->rcdCnt<1) return 0;
$left=1;
$right=$this->rcdCnt;
while($left<$right)//实行二分查找
{
$mid=(int)(($left+$right)/2);
if ($mid==$left or $mid==$right) break;
$rcd=$this->getRcd($mid);
if ($rcd[id]==$id) return $mid;
else if($id<$rcd[id]) $right=$mid;
else $left=$mid;
}
$rcd=$this->getRcd($left);
if ($rcd[id]==$id) return $left;
$rcd=$this->getRcd($right);
if ($rcd[id]==$id) return $right;
//查找胜利前往地位编号N
return 0;//掉败前往0
}
/*从数据库中删除编号为ID的纪录*/
function delete($id)
{
//查找此纪录在数据库中的地位编号
$rid=$this->findByID($id);
if (!$rid) return;//不存在ID号为id的纪录
$res=$this->getRcd($rid);//获得此纪录的定位信息
//从索引文件中删除此纪录的定位信息
fseek($this->indxh,($rid+1)*12);
$str=fread($this->indxh,($this->rcdCnt-$i)*12);
fseek($this->indxh,$rid*12);
fwrite($this->indxh,$str);
//更新数据库纪录个数并永世写入数据库
$this->rcdCnt--;
$this->setRcd(0,$this->rcdCnt,$this->maxID,$this->DBend);
//将此纪录在dbf所占用的空间挂号到闲置空间队列
$this->leftCnt++;
$this->setLeft(0,$this->leftCnt,0);
$this->setLeft($this->leftCnt,$res[loc],$res[len]);
}
/*更新ID编号为id的纪录内容*/
/*len用于从头限制纪录的内容*/
function update($id,$newcontent,$len=0)
{
//将ID编号转化为地位编号N
$rid=$this->findByID($id);
if (!$rid) return;//不存的ID编号
if (!$len) $len=strlen($newcontent);
//获得此纪录定位信息
$rcd=$this->getRcd($rid);
//更新的内容长度超越纪录本来分派的空间
if ($rcd[len]<$len)
{
//保持原空间并将此空间录入闲置空间队列
$this->leftCnt++;
$this->setLeft(0,$this->leftCnt,0);
$this->setLeft($this->leftCnt,$rcd[loc],$rcd[len]);
//在dbf末尾为此纪录从头分派空间
$rcd[loc]=$this->DBend;
$rcd[len]=$len;
$this->DBend+=$len;
//更新数据库信息
$this->setRcd(0,$this->rcdCnt,$this->maxID,$this->DBend);
$this->setRcd($rid,$rcd[id],$rcd[loc],$rcd[len]);
}
//写入新数据
fseek($this->dbh,$rcd[loc]);
fwrite($this->dbh,$newcontent,$len);
}
/*依据地位编号获得纪录内容*/
function selectByRid($rid)
{
//数据以ID编号与实践数据content二元组前往
$res=array('id'=>0,'content'=>'');
//毛病的地位编号
if ($rid<1 or $rid>$this->rcdCnt) return $res;
//读取定位信息
else $rcd=$this->getRcd($rid);
$res[id]=$rcd[id];
$res[len]=$rcd[len];
//依据定位信息从dbf中读取实践数据
fseek($this->dbh,$rcd[loc]);
$res[content]=fread($this->dbh,$rcd[len]);
return $res;
}
/*依据ID编号获得纪录内容*/
function select($id)
{
//将ID编号转换成地位编号再挪用下面的函数
return $this->selectByRid($this->findByID($id));
}
/*数据库备份*/
function backup()
{
copy($this->path.'/'.$this->name.'.tdb',$this->path.'/'.$this->name.'.tdb.bck');
copy($this->path.'/'.$this->name.'.indx',$this->path.'/'.$this->name.'.indx.bck');
copy($this->path.'/'.$this->name.'.lft',$this->path.'/'.$this->name.'.lft.bck');
}
/*从备份中恢复*/
function recover()
{
copy($this->path.'/'.$this->name.'.tdb.bck',$this->path.'/'.$this->name.'.tdb');
copy($this->path.'/'.$this->name.'.indx.bck',$this->path.'/'.$this->name.'.indx');
copy($this->path.'/'.$this->name.'.lft.bck',$this->path.'/'.$this->name.'.lft');
}
/*排除数据库*/
function drop()
{
@unlink($this->path.'/'.$this->name.'.tdb');
@unlink($this->path.'/'.$this->name.'.indx');
@unlink($this->path.'/'.$this->name.'.lft');
}
/*清空数据库纪录*/
function reset()
{
setRcd(0,0,0);
setLeft(0,0);
}
}
?>
PHP成功的插入,删除,更新数据的时候,显然,你已经距离成功指日可待了。 |
|