首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >纯 PHP 向量数据库 PHPVector

纯 PHP 向量数据库 PHPVector

作者头像
Tinywan
发布2026-07-01 15:37:07
发布2026-07-01 15:37:07
890
举报
文章被收录于专栏:开源技术小栈开源技术小栈

PHPVector

一个纯 PHP 向量数据库,实现了 HNSW(Hierarchical Navigable Small World)用于近似最近邻搜索,以及 BM25 用于全文检索。两种引擎可以组合成一个单一的 混合搜索(hybrid search) 管道。

要求

  • PHP 8.2+
  • 核心功能无需任何外部 PHP 扩展
  • ext-pcntl(可选)。启用异步文档写入,可显著降低插入延迟

安装

代码语言:javascript
复制
composer require ezimuel/phpvector

快速开始

1. 插入文档

Document 对象包含密集嵌入向量、可选的原始文本(用于 BM25)以及你希望随结果返回的任意元数据。id 字段可选——如果省略,将自动分配一个随机的 UUID v4。

代码语言:javascript
复制
use PHPVector\Document;
use PHPVector\VectorDatabase;

$db = new VectorDatabase();

$db->addDocuments([
    new Document(
        id: 1,
        vector: [0.12, 0.85, 0.44, 0.67],
        text: 'PHP vector database with HNSW index',
        metadata: ['url' => 'https://example.com/1', 'lang' => 'en'],
    ),
    new Document(
        id: 2,
        vector: [0.91, 0.23, 0.78, 0.05],
        text: 'Approximate nearest neighbour search in PHP',
        metadata: ['url' => 'https://example.com/2', 'lang' => 'en'],
    ),
    new Document(
        id: 3,
        vector: [0.33, 0.61, 0.19, 0.88],
        text: 'BM25 full-text ranking algorithm explained',
        metadata: ['url' => 'https://example.com/3', 'lang' => 'en'],
    ),
    // 未提供 id 时,会自动分配 UUID v4
    new Document(
        vector: [0.55, 0.42, 0.71, 0.30],
        text: 'Hybrid search with Reciprocal Rank Fusion',
    ),
]);

2. 向量搜索

使用 HNSW 查找与查询向量最相似的 k 个文档。

代码语言:javascript
复制
$queryVector = [0.10, 0.80, 0.50, 0.60];

$results = $db->vectorSearch(vector: $queryVector, k: 2);

foreach ($results as $result) {
    echo sprintf(
        "[%d] score=%.4f  %s\n",
        $result->rank,
        $result->score,
        $result->document->metadata['url'],
    );
}
// 输出示例:
// [1] score=0.9987  https://example.com/1
// [2] score=0.8341  https://example.com/3

3. 全文搜索

使用 BM25 根据文本查询对文档进行相关性排序。

代码语言:javascript
复制
$results = $db->textSearch(query: 'nearest neighbour PHP', k: 2);

foreach ($results as $result) {
    echo sprintf(
        "[%d] score=%.4f  %s\n",
        $result->rank,
        $result->score,
        $result->document->metadata['url'],
    );
}
// 输出示例:
// [1] score=1.2430  https://example.com/2
// [2] score=0.8761  https://example.com/1

4. 混合搜索

将向量相似度分数和 BM25 分数融合成一个统一的排序列表。

推荐方式

RRF 基于排名且与分数尺度无关,无需调参。

代码语言:javascript
复制
use PHPVector\HybridMode;

$results = $db->hybridSearch(
    vector: $queryVector,
    text:   'vector database PHP',
    k:      3,
    mode:   HybridMode::RRF,
);

foreach ($results as $result) {
    echo sprintf(
        "[%d] score=%.4f  %s\n",
        $result->rank,
        $result->score,
        $result->document->metadata['url'],
    );
}
加权组合

将两种分数都归一化到 [0, 1] 区间,然后应用显式权重。

代码语言:javascript
复制
$results = $db->hybridSearch(
    vector:       $queryVector,
    text:         'vector database PHP',
    k:            3,
    mode:         HybridMode::Weighted,
    vectorWeight: 0.7,
    textWeight:   0.3,
);

配置

HNSW 和 BM25 引擎均可完全配置。将配置对象传递给 VectorDatabase 构造函数即可。

代码语言:javascript
复制
use PHPVector\BM25\ConfigasBM25Config;
use PHPVector\BM25\SimpleTokenizer;
use PHPVector\Distance;
use PHPVector\HNSW\ConfigasHNSWConfig;
use PHPVector\VectorDatabase;

$db = new VectorDatabase(
    hnswConfig: new HNSWConfig(
        M:               16,    // 每层每个节点的最大连接数。越高 → 召回率越好,但内存占用增加。
        efConstruction:  200,   // 构建索引时的束宽。越高 → 图质量越好,但插入变慢。
        efSearch:        50,    // 查询时的束宽。越高 → 召回率越好,但查询变慢。
        distance:        Distance::Cosine, // 支持:Cosine | Euclidean | DotProduct | Manhattan
        useHeuristic:    true,  // 多样化邻居选择(推荐启用)。
    ),
    bm25Config: new BM25Config(
        k1: 1.5,   // TF 饱和度。推荐范围 1.2–2.0。
        b:  0.75,  // 长度归一化。0 = 不归一化,1 = 完全归一化。
    ),
    tokenizer: new SimpleTokenizer(
        stopWords:      SimpleTokenizer::DEFAULT_STOP_WORDS,
        minTokenLength: 2,
    ),
);

距离度量

度量

最适用场景

Distance::Cosine

文本嵌入、已归一化向量

Distance::Euclidean

原始未归一化向量

Distance::DotProduct

单位归一化向量(比 Cosine 更快)

Distance::Manhattan

稀疏向量,对异常值更鲁棒

HNSW 调优速查表

目标

调整参数

提高召回率

增大 efSearch 或 efConstruction

加快查询速度

减小 efSearch

减少内存占用

减小 M

处理聚类数据时获得更好图结构

保持 useHeuristic: true

持久化

PHPVector 采用基于文件夹的持久化模型。每个数据库对应一个独立目录,里面包含 HNSW 图、BM25 索引以及每个文档单独的文件。这种设计有两个主要优势:

  • 加载时内存占用低 —— 仅将 HNSW 图和 BM25 索引加载到内存。单个文档文件(docs/{n}.bin)采用懒加载,仅在搜索结果中出现时才读取。
  • 插入延迟低 —— 文档文件通过子进程异步写入磁盘(需要 ext-pcntl),因此 addDocument() 调用会立即返回。

文件夹结构

代码语言:javascript
复制
/var/data/mydb/
  meta.json       — 距离度量、维度、文档 ID 映射
  hnsw.bin        — HNSW 图(向量 + 连接关系)
  bm25.bin        — BM25 倒排索引
  docs/
    0.bin         — 文档 0(id、文本、元数据)
    1.bin         — 文档 1
    …

保存

在构造函数中传入 path 参数即可启用持久化。每次 addDocument() 调用都会将文档文件写入 docs/ 目录(启用 ext-pcntl 时异步进行)。最后调用一次 save() 将 HNSW 图和 BM25 索引刷新到磁盘(会等待所有异步写入完成)。

代码语言:javascript
复制
use PHPVector\Document;
use PHPVector\VectorDatabase;

$db = new VectorDatabase(path: '/var/data/mydb');

$db->addDocuments([
    new Document(id: 1, vector: [0.12, 0.85, 0.44], text: 'PHP vector search', metadata: ['source' => 'blog']),
    new Document(id: 2, vector: [0.91, 0.23, 0.78], text: 'Approximate nearest neighbour'),
    // ... 可以插入数千个文档
]);

// 将 HNSW 图和 BM25 索引刷盘(文档文件已异步写入)
$db->save();

加载

使用 VectorDatabase::open() 加载已保存的数据库(文档加载部分此处原文被截断,实际使用时可参考源码或后续更新)。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2026-03-25,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 开源技术小栈 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • PHPVector
  • 要求
  • 安装
  • 快速开始
    • 1. 插入文档
    • 2. 向量搜索
    • 3. 全文搜索
    • 4. 混合搜索
      • 推荐方式
      • 加权组合
  • 配置
    • 距离度量
    • HNSW 调优速查表
  • 持久化
    • 文件夹结构
    • 保存
    • 加载
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档