基于times33算法的hash数据库

本地缓存数据库设计原理
1、分两个文件一个索引文件 .idx,一个数据文件 .dat
2、索引文件结构 : [索引指针 | 索引指针| …|索引指针|索引记录|…|索引记录]
索引指针:用来记录索引记录的偏移量(当时的文件size)
索引记录:hash链表 结构=》 [下一个借点指针 | key| 数据偏移量(当时的文件size)|数据长度]
代码如下:
具体说明:
0、初始化的时候创建索引和数据文件
(索引文件组成:索引(存放hash偏移量)+ 索引块(4字节的下一个hash的偏移量+key(key 128位长度不足补0)+dataSize+dataLenth))
(说明:索引(用key的time33算法后得到的二进制数据去读取,长度4字节)(存储的是当前文件的size值)
索引记录块(用索引记录的值去读取长度,128字节)

//获取数据
1、输入key,用time33算法计算出key的偏移量
2、通过key的偏移量,获取hash记录块的偏移量
3、通过hash记录块偏移量获取hash记录块数据。判断key是否相等,不等就读取下一个记录块,直到找到,
4、通过data偏移量(当时的文件size)和data的长度,获取data的数据

//数据插入
1、fstat()获取索引文件的文件size,数据文件的文件size
2、构造hash索引记录块(4字节的下一个hash的偏移量+key(key 128位长度不足补0)+dataSize+dataLenth)
3、读取key索引偏移量(当时文件的size)去获取hash记录块,如果没有就直接插入记录块,如果有就第4步
4、读取已经有的hash记录块,判断key值是否相等,如果相等,就报存在。如果不等统插入索引块,数据。

//数据删除
找到数据,如果是头节点,就修改索引里存放的偏移量为下一个索引块的偏移量,如果不是就交换节点。
亲测可用
不足:1、没时间做并发控制。2、删除索引以后数据文件没有改变。
针对以上两个问题,有知道比较好做法的小伙伴留言哈。

<?php

define('DB_INSERT','1');
define('DB_REPALCE','2');
define('DB_STORE','3');
define('DB_BUCKET_SIZE','262144');
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='false';//关闭标识

    public function __construct($pathname)
    {
        if($pathname){
            $this->open($pathname);
        }
    }

    public function __destruct()
    {
        $this->close();
    }

    /**打开索引文件和数组文件
     * @since 2018/12/14
     * @author weihua.zhang
     * @param $pathname
     * @return string
     */
	public function open($pathname){
		$idx_path = $pathname.'.idx';
		$dat_path = $pathname.'.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(!$this->idx_fp){
			return DB_FAILURE;
		}
		//初始化索引块
		if($init){
			$elem = pack('L',0x00000000);//128字节
			for($i=0;$i<DB_BUCKET_SIZE;$i++){
				fwrite($this->idx_fp, $elem,4);
			}
		}
		//打开文件,如果没有就创建
		$this->dat_fp = fopen($dat_path, $mode);
		if(!$this->idx_fp){
			return DB_FAILURE;
		}

		return DB_SUCCESS;

	}

    /**key偏移量计算
     * @since 2018/12/14
     * @author weihua.zhang
     * @param $key
     * @return float|int
     */
	private function _offset($key){
		return $this->_hash($key)%DB_BUCKET_SIZE*4;
	}

    /**times33算法
     * @since 2018/12/14
     * @author weihua.zhang
     * @param $string
     * @return int
     */
	private function _hash($string){
		$string = substr(md5($string), 0,8);
		$hash = 0;
		for ($i=0; $i <8 ; $i++) { 
			$hash += 33*$hash + ord($string{$i});
		}
		return $hash&0X7FFFFFFF;
	}

    /**读文件
     * @since 2018/12/14
     * @author weihua.zhang
     * @param $handle 文件句柄
     * @param $offset 偏移量
     * @param $lenth 长度
     * @return bool|string
     */
	private function _fileRead($handle,$offset,$lenth){
        fseek($handle, $offset,SEEK_SET);//设置文件光标位置
        return fread($handle, $lenth);
    }

    /**写文件
     * @since 2018/12/14
     * @author weihua.zhang
     * @param $handle 文件句柄
     * @param $offset 偏移量
     * @param $data 数据
     * @param $lenth 长度
     */
    private function _fileWrite($handle,$offset,$data,$lenth){
        fseek($handle, $offset,SEEK_SET);
        fwrite($handle, $data,$lenth);
    }

    /**获取数据操作
     * @since 2018/12/14
     * @author weihua.zhang
     * @param $key
     * @return bool|string
     */
	public function fetch($key){
		$offset = $this->_offset($key);
		$pos = unpack('L', $this->_fileRead($this->idx_fp,$offset,4));//获取hash链表的偏移量
		$pos = $pos[1];
		//查找数据
		$found = false;
		while ($pos){
			$block = $this->_fileRead($this->idx_fp,$pos,DB_INDEX_SIZE);//获取索引记录
            $cpKey = substr($block, 4,DB_KEY_SIZE);
			//找到数据
			if(!strncmp($key, $cpKey, strlen($key))){
				$dataoff = unpack('L', substr($block, DB_KEY_SIZE+4,4));
				$dataoff = $dataoff[1];//数据偏移量
				$datalen = unpack('L', substr($block, DB_KEY_SIZE+8,4));
				$datalen = $datalen[1];//数据长度
				$found = true;
				break;
			}
			//没找到数据,就查找下一个索引记录
			$pos = unpack('L', substr($block, 0,4));//获取hash链表的偏移量
			$pos = $pos[1];
		}

		if(!$found){
			return DB_FAILURE;
		}
        $data = $this->_fileRead($this->dat_fp,$dataoff,$datalen);
		return $data;
	}

    /**插入数据
     * @since 2018/12/14
     * @author weihua.zhang
     * @param $block 索引记录
     * @param $data  存储的数据
     * @param $offset 索引偏移量
     * @param $idxoff 新增索引记录偏移量
     * @param $datoff 数据偏移量
     */
	private function _insert_data($block,$data,$offset,$idxoff,$datoff){
        $this->_fileWrite($this->idx_fp,$offset,pack('L',$idxoff),4);//新增索引
        $this->_fileWrite($this->idx_fp,$idxoff,$block,DB_INDEX_SIZE);//添加索引记录
        $this->_fileWrite($this->dat_fp,$datoff,$data,strlen($data));//添加数据
	}

    /**写入操作
     * @since 2018/12/14
     * @author weihua.zhang
     * @param $key
     * @param $data
     * @return string
     */
	public function insert($key,$data){
		$offset = $this->_offset($key);
		$idxoff = fstat($this->idx_fp);//获取索引表的统计信息,计算索引记录的偏移量
		$idxoff = $idxoff['size'];
		$datoff = fstat($this->dat_fp);//获取数据表的统计信息,计算数据记录的偏移量
		$datoff = $datoff['size'];
		$keylen = strlen($key);
		if($keylen>DB_KEY_SIZE){
			return DB_FAILURE;
		}
		//构造索引记录块
		$block = pack('L',0000);
		$block.= $key;
		$space = DB_KEY_SIZE-$keylen;
		for ($i=0; $i < $space; $i++) { 
			$block.= pack('C',0);
		}
		$block .= pack('L',$datoff);
		$block .= pack('L',strlen($data));
		//判断key是否存在,如果没有就写入索引记录的偏移量
		$pos = unpack('L', $this->_fileRead($this->idx_fp,$offset,4));
		$pos = $pos[1];
		if($pos==0){
            $this->_insert_data($block,$data,$offset,$idxoff,$datoff);
            return DB_SUCCESS;
		}
		$found = false;
		while ($pos) {
			//获取索引记录块
			$temp_block = $this->_fileRead($this->idx_fp,$pos,DB_INDEX_SIZE);
			$cpKey = substr($temp_block, 4,DB_KEY_SIZE);
			if(!strncmp($key, $cpKey, strlen($key))){
				$found = true;
				break;
			}
            $prev = $pos;//记录本次的偏移量,因为下次索引记录可能是空的
            $pos = unpack('L',substr($temp_block, 0,4));
            $pos = $pos[1];
		}
        if($found){
            return DB_KEY_EXISTS;
        }
        $this->_insert_data($block,$data,$prev,$idxoff,$datoff);
        return DB_SUCCESS;
	}

    /**删除操作
     * @since 2018/12/14
     * @author weihua.zhang
     * @param $key
     * @return string
     */
	public function delete($key){
        $offset = $this->_offset($key);
        $head = unpack('L',$this->_fileRead($this->idx_fp,$offset,4));
        $curr = $head = $head[1];
        $prev = 0;
        $next = 0;
        $datoff = 0;
        $datlen = 0;
        //获取索引记录
        while ($curr){
            $block = $this->_fileRead($this->idx_fp,$curr,DB_INDEX_SIZE);
            $next = unpack('L',substr($block,0,4))[1];
            $cpkey = substr($block,4,DB_KEY_SIZE);
            if(!strncmp($key,$cpkey,strlen($key))){
                $datoff = unpack('L',substr($block,DB_KEY_SIZE+4,4))[1];
                $datlen = unpack('L',substr($block,DB_KEY_SIZE+8,4))[1];
                $found = true;
                break;
            }
            $prev = $curr;
            $curr = $next;
        }
        if(!$found){
            return DB_FAILURE;
        }
        if($prev==0){
            //如果是头节点,修改索引为下一个hash链表的借点
            $this->_fileWrite($this->idx_fp,$offset,pack('L',$next),4);
        }else{
            //如果不是头节点,交换借点
            $this->_fileWrite($this->idx_fp,$prev,pack('L',$next),4);
        }
        return DB_SUCCESS;

    }

    /**关闭句柄
     * @since 2018/12/14
     * @author weihua.zhang
     */
    public function close(){
	    if(!$this->closed){
	        fclose($this->idx_fp);
	        fclose($this->dat_fp);
            $this->closed = true;
        }
    }

	
}


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值