PHP使用hash算法实现一个简单的数据库代码实例

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/luyaran/article/details/85122802

咱们这次主要是使用PHP语言,结合hash算法,来实现一个简单的数据库实例,它主要有四个功能,连接数据库,查询操作,插入操作,删除操作,关闭数据库连接操作,其它的大家可以后期补充完善下,咱废话不多说,先来看代码:

<?php
header('Content-type:text/html;charset=utf-8');

define('DB_INSERT',1);
define('DB_REPLACE',2);
define('DB_STORE',3);
define('DB_BUCKET_SIZE',262114);
define('DB_KEY_SIZE',128);
define('DB_INDEX_SIZE',DB_KEY_SIZE+12);
define('DB_KEY_EXISTS',1);
define('DB_FAILURE',-1);
define('DB_SUCCESS',0);

class DB{
    private $idx_fp;
    private $dat_fp;
    private $closed;

    public function open($path_name)
    {
        $idx_path = $path_name.".idx";
        $dat_path = $path_name.".dat";

        if (!file_exists($idx_path)) {
            $init = true;
            $mode = 'w+b';
        }else{
            $init = false;
            $mode = 'r+b';
        }

        $this->idx_fp = fopen($idx_path,$mode);
        if($init){
            $elem = pack('L',0x00000000);
            for ($i=0;$i<DB_BUCKET_SIZE;$i++) {
                fwrite($this->idx_fp,$elem,4);
            }
        }

        $this->dat_fp = fopen($dat_path,$mode);
        if(!file_exists($dat_path)) {
            return DB_FAILURE;
        }
        if(!$this->dat_fp) {
            return DB_FAILURE;
        }
        return DB_SUCCESS;
    }

    private function _hash($str)
    {
        $str = substr(md5($str),0,8);
        $hash = 0;
        for ($i=0;$i<8;$i++) {
            $hash += 33*$hash+ord($str[$i]);
        }
        return $hash&0x7FFFFFFF;
    }

    public function fetch($key)
    {
        $offset = ($this->_hash($key)%DB_BUCKET_SIZE)*4;
        fseek($this->idx_fp,$offset,SEEK_SET);
        $pos = unpack('L',fread($this->idx_fp,4));
        $pos = $pos[1];
        $found = false;
        while ($pos) {
            fseek($this->idx_fp,$pos,SEEK_SET);
            $block = fread($this->idx_fp,DB_INDEX_SIZE);
            $cp_key = substr($block,4,DB_KEY_SIZE);
            if (!strncmp($key,$cp_key,strlen($key))) {
                $data_off = unpack('L',substr($block,DB_KEY_SIZE+4,4));
                $data_off = $data_off[1];
                $data_len = unpack('L',substr($block,DB_KEY_SIZE+8,4));
                $data_len = $data_len[1];
                $found = true;
                break;
            }

            $pos = unpack('L',substr($block,0,4));
            $pos = $pos[1];
        }

        if(!$found) {
            return null;
        }

        fseek($this->dat_fp,$data_off,SEEK_SET);
        $data = fread($this->dat_fp,$data_len);
        return $data;
    }

    public function insert($key,$data)
    {
        $offset = ($this->_hash($key)%DB_BUCKET_SIZE)*4;
        $idx_off = fstat($this->idx_fp);
        $idx_off = intval($idx_off['size']);
        $dat_off = fstat($this->dat_fp);
        $dat_off = intval($dat_off['size']);
        $key_len = strlen($key);
        if($key_len > DB_KEY_SIZE) {
            return DB_FAILURE;
        }

        $block = pack('L',0x00000000);
        $block .= $key;
        $space = DB_KEY_SIZE-$key_len;
        for ($i=0;$i<$space;$i++) {
            $block .= pack('C',0x00);
        }
        $block .= pack('L',$dat_off);
        $block .= pack('L',strlen($data));

        fseek($this->idx_fp,$offset,SEEK_SET);
        $pos = unpack('L',fread($this->idx_fp,4));
        $pos = $pos[1];

        if ($pos == 0) {
            fseek($this->idx_fp,$offset,SEEK_SET);
            fwrite($this->idx_fp,pack('L',$idx_off),4);
            fseek($this->idx_fp,0,SEEK_END);
            fwrite($this->idx_fp,$block,DB_INDEX_SIZE);
            fseek($this->dat_fp,0,SEEK_END);
            fwrite($this->dat_fp,$data,strlen($data));
            return DB_SUCCESS;
        }

        $found = false;
        while ($pos) {
            fseek($this->idx_fp,$pos,SEEK_SET);
            $tmp_block = fread($this->idx_fp,DB_INDEX_SIZE);
            $cp_key = substr($tmp_block,4,DB_KEY_SIZE);
            if(!strncmp($key,$cp_key,strlen($key))) {
                $data_off = unpack('L',substr($tmp_block,DB_KEY_SIZE+4,4));
                $data_off = $data_off[1];
                $data_len = unpack('L',substr($tmp_block,DB_KEY_SIZE+8,4));
                $data_len = $data_len[1];
                $found = true;
                break;
            }

            $prev = $pos;
            $pos = unpack('L',substr($tmp_block,0,4));
            $pos = $pos[1];
        }

        if ($found) {
            return DB_KEY_EXISTS;
        }

        fseek($this->idx_fp,$prev,SEEK_SET);
        fwrite($this->idx_fp,pack('L',$idx_off),4);
        fseek($this->idx_fp,0,SEEK_END);
        fwrite($this->idx_fp,$block,DB_INDEX_SIZE);
        fseek($this->dat_fp,0,SEEK_END);
        fwrite($this->dat_fp,$data,strlen($data));
        return DB_SUCCESS;
    }

    public function delete($key)
    {
        $offset = ($this->_hash($key)%DB_BUCKET_SIZE)*4;
        fseek($this->idx_fp,$offset,SEEK_SET);
        $head = unpack('L',fread($this->idx_fp,4));
        $head = $head[1];
        $curr = $head;
        $prev = 0;

        while ($curr) {
            fseek($this->idx_fp,$curr,SEEK_SET);
            $block = fread($this->idx_fp,DB_INDEX_SIZE);
            $next = unpack('L',substr($block,0,4));
            $next = $next[1];
            $cp_key = substr($block,4,DB_KEY_SIZE);
            if(!strncmp($key,$cp_key,strlen($key))) {
                $found = true;
                break;
            }

            $prev = $curr;
            $curr = $next;
        }

        if(!$found){
            return DB_FAILURE;
        }

        if ($prev == 0) {
            fseek($this->idx_fp,$offset,SEEK_SET);
            fwrite($this->idx_fp,pack('L',$next),4);
        }else{
            fseek($this->idx_fp,$prev,SEEK_SET);
            fwrite($this->idx_fp,pack('L',$next),4);
        }

        return DB_SUCCESS;
    }

    public function close()
    {
        if (!$this->closed) {
            fclose($this->idx_fp);
            fclose($this->dat_fp);
            $this->closed = true;
        }
    }
}

上述代码首先是定义了一些咱们需要用到的常量,完事类里面的三个私有的成员变量是两个文件打开的句柄和记录数据库是否连接的句柄,idx的句柄是索引的句柄,另外一个文件句柄是保存数据文件的句柄,之后就是一个open的方法用来代开数据库连接也就是创建索引文件和数据文件,以及检测其是否存在,之后的_hash是一个计算字符串hash值的方法,利用的是timer33算法,之后是读取的方法,再来就是插入和删除的方法,里面具体函数的用处,大家可以在熟悉代码的过程中查百度,咱们这里就不做过多的赘述了。

接下来看下测试增删查的代码实例:

//增
<?php
header('Content-type:text/html;charset=utf-8');
include './db.php';
$db = new DB();
$res = $db->open('test');
$start_time = explode(' ',microtime());
$start_time = $start_time[0]+$start_time[1];
for ($i=0;$i<1000;$i++) {
    $db->insert('key'.$i,'value'.$i);
}
$end_time = explode(' ',microtime());
$end_time = $end_time[0]+$end_time[1];
$db->close();
echo 'process time in '.($end_time-$start_time).' seconds';
die;
//查
<?php
header('Content-type:text/html;charset=utf-8');
include './db.php';
$db = new DB();
$db->open('test');
$start_time = explode(' ',microtime());
$start_time = $start_time[0]+$start_time[1];
for ($i=0;$i<1000;$i++) {
    $data = $db->fetch('key'.$i);
//    echo $data."<br>";
}
$end_time = explode(' ',microtime());
$end_time = $end_time[0]+$end_time[1];
$db->close();
echo 'process time in '.($end_time-$start_time).' seconds';
die;
//删
<?php
header('Content-type:text/html;charset=utf-8');
include './db.php';
$db = new DB();
$db->open('test');
$start_time = explode(' ',microtime());
$start_time = $start_time[0]+$start_time[1];
$db->delete('key1');
for ($i=0;$i<1000;$i++) {
    $data = $db->fetch('key'.$i);
//    echo $data."<br>";
}
$end_time = explode(' ',microtime());
$end_time = $end_time[0]+$end_time[1];
$db->close();
echo 'process time in '.($end_time-$start_time).' seconds';
die;

插入和查询都已按着一千条来计算的,速度么,还算是可以的,都是毫秒级别的,大家有兴趣的可以多多测试和完善下,咱这里就算是抛砖引玉了,各位有什么问题和想法都可以给我评论和私信,只要看到了必回。

好啦,本次记录就到这里了,如果感觉不错的话,请多多点赞支持哦。。。

猜你喜欢

转载自blog.csdn.net/luyaran/article/details/85122802