bai_jimmy

导航

通过本地文件数据库查询手机归属地

<?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);
    }

}

?>

 

posted on 2013-11-01 09:45  bai_jimmy  阅读(836)  评论(0编辑  收藏  举报