通过本地文件数据库查询手机归属地
<?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); } } ?>