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

}

?>

 

分类:

技术点:

相关文章:

  • 2021-12-05
  • 2021-11-23
  • 2021-06-25
  • 2021-09-20
  • 2022-01-10
猜你喜欢
  • 2021-11-23
  • 2021-11-23
  • 2021-12-08
  • 2021-06-27
相关资源
相似解决方案