<?php /** * 40W数据最多需要19次查找 * 如果顺序查找的话26W数据最多要用9.28s * 二分查找法的话26W数据最多要用0.067s */ define('KEY_SIZE', 12); //键值的大小 class Mobile { private $idx_fp; private $dat_fp; private $idx_length; private $dat_length; public function __construct($filename = './mobile') { $idxfile = $filename . '.idx'; $datfile = $filename . '.dat'; $mode = file_exists($idxfile) ? 'r+b' : 'w+b'; $this->idx_fp = fopen($idxfile, $mode); $this->dat_fp = fopen($datfile, $mode); } public function set($key, $value) { if ($this->_lock(true)) { $this->_setLength(); $idxData = pack('L3', $key, $this->dat_length, strlen($value)); //索引的结构:键值+数据的数据偏移量+数据长度 $this->_put($this->idx_fp, $this->idx_length, $idxData, KEY_SIZE); $this->_put($this->dat_fp, $this->dat_length, $value, strlen($value)); //写入数据 $this->_unlock(); } else { $this->_trigger_error('Could not lock this file !', E_USER_ERROR); return false; } } public function get($key) { $found = false; $this->_setLength(); $start = 0; $end = ($this->idx_length-KEY_SIZE)/KEY_SIZE-1; while ($start <= $end) { $mid = ceil(($end + $start) / 2); $keyInfo = unpack('Lkey/Loffset/Llength', $this->_read($this->idx_fp, $mid*KEY_SIZE, KEY_SIZE)); if ($keyInfo['key'] == $key) { $found = true; break; } elseif ($keyInfo['key'] < $key) { $start=$mid+1; } else { $end=$mid-1; } } if ($found) { return $this->_read($this->dat_fp, $keyInfo['offset'], $keyInfo['length']); } return false; } private function _seek($fp, $offset) { fseek($fp, $offset, SEEK_SET); } private function _put($fp, $offset, $data, $size) { $this->_seek($fp, $offset); fwrite($fp, $data, $size); } private function _read($fp, $offset, $size) { flock($fp, LOCK_SH); $this->_seek($fp, $offset); return fread($fp, $size); flock($fp, LOCK_UN); } private function _setLength() { clearstatcache(); $info = fstat($this->idx_fp); $this->idx_length = $info['size']; $info = fstat($this->dat_fp); $this->dat_length = $info['size']; } /** * 锁定文件操作 * @param type $isblock 是否堵塞 */ private function _lock($isblock) { $dFlock = flock($this->dat_fp, $isblock ? LOCK_EX : LOCK_EX + LOCK_NB); $iFlock = flock($this->idx_fp, $isblock ? LOCK_EX : LOCK_EX + LOCK_NB); return $dFlock && $iFlock; } private function _unlock() { $dFlock = flock($this->dat_fp, LOCK_UN); $iFlock = flock($this->idx_fp, LOCK_UN); } private function _trigger_error($error_msg, $error_type) { trigger_error($error_msg, $error_type); } } ?>
这是初步代码,后期慢慢完善一下 演示地址:http://www.idoushuo.com/mobile/search.php
最近又把做的手机归属地这个想了想,现在基于haseTable重写了一下,进行1w次查询,效率提升了一个量级(5.35524392128->0.413395023346),代码如下:
<?php /* +------------------------------------------------------------------------------ | datetime 2013-11-10 10:46:22 +------------------------------------------------------------------------------ | author baijm +------------------------------------------------------------------------------ | version 1.0 +------------------------------------------------------------------------------ */ class Mobile { private $header_padding = 20; private $file; private $handler; public function __construct($file) { $this->file = dirname(__FILE__) . '/' . $file . '.php'; if (!file_exists($this->file)) { $this->_create(); } else { $this->handler = fopen($this->file, 'r+b'); } } public function set($key, $value) { clearstatcache(); $len = filesize($this->file); $key = substr($key, 0, 7) - 1300000; $this->_put($this->header_padding + $key * 8, pack('VV', $len, strlen($value))); $data = $this->_put($len, $value); return true; } public function get($key) { $key = substr($key, 0, 7) - 1300000; $this->_seek($this->header_padding + $key * 8); list(, $offset, $len) = unpack('V2', fread($this->handler, 8)); if (!$len) { return false; } $this->_seek($offset); return fread($this->handler, $len); } private function _create() { $this->handler = fopen($this->file, 'w+b') or $this->_trigger_error('Can not open the file' . realpath($this->file), E_USER_ERROR); $this->_put(0, '<?php exit(); ?>'); return $this->_format(); } private function _format() { if ($this->_lock(true)) { $this->_put($this->header_padding, str_repeat(pack('VV', 0x0000, 0x0000), 600000)); return true; } else { $this->_trigger_error('Could not lock the file', E_USER_ERROR); return false; } } private function _put($offset, $data) { $this->_seek($offset); return fwrite($this->handler, $data); } private function _lock($block = true) { return flock($this->handler, $biock ? LOCK_EX : LOCK_EX + LOCK_NB); } private function _seek($offset) { fseek($this->handler, $offset, SEEK_SET); } private function _trigger_error($error_msg, $level) { trigger_error($error_msg, $level); } } ?>