1. 程式人生 > >PHP使用hash演算法實現一個簡單的資料庫程式碼例項

PHP使用hash演算法實現一個簡單的資料庫程式碼例項

咱們這次主要是使用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;

插入和查詢都已按著一千條來計算的,速度麼,還算是可以的,都是毫秒級別的,大家有興趣的可以多多測試和完善下,咱這裡就算是拋磚引玉了,各位有什麼問題和想法都可以給我評論和私信,只要看到了必回。

好啦,本次記錄就到這裡了,如果感覺不錯的話,請多多點贊支援哦。。。