PHPackages                             cwjken/filesearch - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. cwjken/filesearch

ActiveProject

cwjken/filesearch
=================

a test

06PHP

Since Apr 27Pushed 8y ago1 watchersCompare

[ Source](https://github.com/cwjken/FileSearch)[ Packagist](https://packagist.org/packages/cwjken/filesearch)[ RSS](/packages/cwjken-filesearch/feed)WikiDiscussions master Synced 2mo ago

READMEChangelogDependenciesVersions (1)Used By (0)

PHP实现文本快速查找 - 二分查找法
===================

[](#php实现文本快速查找---二分查找法)

起因
--

[](#起因)

先说说事情的起因，最近在分析数据时经常遇到一种场景，代码需要频繁的读某一张数据库的表，比如根据地区ID获取地区名称、根据网站分类ID获取分类名称、根据关键词ID获取关键词等。虽然以上需求都可以在原始建表时，通过冗余数据来解决。但仍有部分业务存的只是关联表的ID，数据分析时需要频繁的查表。

所读的表存在共同的特点
-----------

[](#所读的表存在共同的特点)

- 数据几乎不会变更
- 数据量适中，从一万到100多万，如果全加载到内存也不太合适。

纠结的地方
-----

[](#纠结的地方)

在做数据分析时，需要十分频繁的读这些表，每秒有可能需要读上万次。其实内部的数据库集群完全可以胜任，但会对线上业务稍有影响。（你懂得，小公司不可能为离线分析做一套完整的数据存储服务。大部分数据分析还要借助线上的数据集群）

优化方案的思考
-------

[](#优化方案的思考)

有没有一种方式可以不增加线上的压力，同时提供更高效的查询方式？想过redis，但最终选择用文本存储。因为数据分析是一个独立的需求，不希望与现有的redis集群或者其它存储服务有交集。还有一个原因是每次分析的中间结果，对下一次分析并没有很大的实质作用，并不需要把结果持久存储，而且占的内存也会较多。最终使用文本存储，然后用二分来查找。特点，1，存储非常快，虽然redis等nosql服务虽然已经非常快，但仍无法与文本存储相提并论；2，查找的时候使用二分查找，百万条记录查询也可在0.1ms内完成（使用线上的普通硬盘，如果是ssd盘会更快）。

实现步骤
----

[](#实现步骤)

- 将数据库中需要的字段导出到文本

    ```
      方法：使用mysql的phpmyadmin工具，执行sql语句查出主建id和相应字段
      如以上的关键词表： select kid, keyword from keyword
      然后使用phpmyadmin的导出工具，可以快速把结果导出到文本中
      操作截图：

    ```

[![image](https://camo.githubusercontent.com/7b775c2ebcac27e6d3479ebf2c7cc4db1a10e3ecb7718f0243cc2297115485b5/687474703a2f2f6f636767693165636a2e626b742e636c6f7564646e2e636f6d2f30463438424634312d374430312d343846392d383036342d3532454635383731464642412e706e67)](https://camo.githubusercontent.com/7b775c2ebcac27e6d3479ebf2c7cc4db1a10e3ecb7718f0243cc2297115485b5/687474703a2f2f6f636767693165636a2e626b742e636c6f7564646e2e636f6d2f30463438424634312d374430312d343846392d383036342d3532454635383731464642412e706e67)

---

[![image](https://camo.githubusercontent.com/c9b0f43018ef8e6185a7e9e6685931ba80035ebc1ab7d6ff8701db9f5036b519/687474703a2f2f6f636767693165636a2e626b742e636c6f7564646e2e636f6d2f37393432313231342d453933412d343644332d393134382d3544394632413638463235452e706e67)](https://camo.githubusercontent.com/c9b0f43018ef8e6185a7e9e6685931ba80035ebc1ab7d6ff8701db9f5036b519/687474703a2f2f6f636767693165636a2e626b742e636c6f7564646e2e636f6d2f37393432313231342d453933412d343644332d393134382d3544394632413638463235452e706e67)

- 将导出的文本（已经按id进行过排序）转换格式重新存储
- 程序读取转换后的格式

文本存储格式
------

[](#文本存储格式)

说明 ：需求中，文本每行有两列，第一列是主建ID（数字），第二列为文本。整个文本已经按第一列有序排列，两列之间用tab键分隔。 之前有看过ip.dat的存储，本次仿照其存储格式：将文本中的内容每行转换为固定长度后，存储到新的文件。搜索时，使用文件操作函数fopen，fseek，fgets等函数按字节读取内容，并以二分查找法快速定位需要的内容。

代码实现部分
------

[](#代码实现部分)

- 通用类，类似需求只需要提供符合标准的文本（每行两列，第一列为查找的ID，第二列为文本。同时文本已经按第一列有序排序）
- 生成以上所提到的存储格式
- 提供根据id查询接口

代码片断
----

[](#代码片断)

- 重新生成新的存储格式

    ```
      //读源文件，写入到新的索引文件
      $readfd = fopen($this->filename, 'rb');
      $writefd = fopen($this->formatFile.'_tmp', 'wb+');
      if ($readfd === false || $writefd === false) {
          return false;
      }
      echo "\n start reformat file $this->filename ..";
      while (!feof($readfd)) {
          $line = fgets($readfd, 8192);
          fwrite($writefd, pack("a".$this->maxLength, $line));
      }
      echo "\n reformat ok\n";
      fclose($readfd);
      fclose($writefd);
      rename($this->formatFile.'_tmp', $this->formatFile);

    ```
- 二分查找的代码片断

    ```
      /**
      * 在索引文件中进行二分查找
      * @param  int $id    进行二分查找的id
      * @return [type]     [description]
      */
      public function search($key)
      {
          $filesize = filesize($this->formatFile);
          $fd = fopen($this->formatFile, "rb");
          $left = 0; //行号
          $right = ($filesize / $this->maxLength) - 1;
          while ($left maxLength);
              $info = unpack("a*", fread($fd, $this->maxLength))['1'];
              $lineinfo = explode("\t", $info, 2);
              if ($lineinfo['0'] > $key) {
                  $right = $middle - 1;
              } elseif ($lineinfo['0'] < $key) {
                  $left = $middle + 1;
              } else {
                  return $lineinfo['1'];
              }
          }
          return false;
      }

    ```
- 整个类库代码一共91行，具体可查看github的demo代码

运行截图
----

[](#运行截图)

[![image](https://camo.githubusercontent.com/d7c242fbe8dd8fed40f2389d72f06740e13ec9d45705faa718b73aecabe57ba6/687474703a2f2f6f636767693165636a2e626b742e636c6f7564646e2e636f6d2f73732e706e67)](https://camo.githubusercontent.com/d7c242fbe8dd8fed40f2389d72f06740e13ec9d45705faa718b73aecabe57ba6/687474703a2f2f6f636767693165636a2e626b742e636c6f7564646e2e636f6d2f73732e706e67)以上拿100万的关键词进行测试，根据关键词id快速查找关键词，平均速度可以达到0.1毫秒。

###  Health Score

20

—

LowBetter than 14% of packages

Maintenance20

Infrequent updates — may be unmaintained

Popularity4

Limited adoption so far

Community9

Small or concentrated contributor base

Maturity41

Maturing project, gaining track record

 Bus Factor1

Top contributor holds 50% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

### Community

Maintainers

![](https://www.gravatar.com/avatar/94ef9d541007aec80e81e094d37ea3ff9926c10696513dd338a9766b5ebad9f8?d=identicon)[cwjken](/maintainers/cwjken)

---

Top Contributors

[![cwjken](https://avatars.githubusercontent.com/u/14371938?v=4)](https://github.com/cwjken "cwjken (3 commits)")[![youyoubao](https://avatars.githubusercontent.com/u/3317966?v=4)](https://github.com/youyoubao "youyoubao (3 commits)")

### Embed Badge

![Health badge](/badges/cwjken-filesearch/health.svg)

```
[![Health](https://phpackages.com/badges/cwjken-filesearch/health.svg)](https://phpackages.com/packages/cwjken-filesearch)
```

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
