概述
还在为外部嵌入API的延迟和费用烦恼吗?或者觉得为向量搜索单独维护一个数据库服务过于臃肿?如果你是一名 PHP 开发者,并且你的 AI 应用对延迟和成本极其敏感,那么 MemVector 或许就是你正在寻找的答案。
MemVector 是一个专为 PHP 设计的高性能扩展,它将 AI 基础设施直接搬进了你的 PHP 进程。它本质上是一个本地的向量数据库和嵌入引擎,所有操作——包括文本向量化、存储、相似性搜索乃至交叉编码器重排序——都在进程内完成,完全跳过了网络往返。它使用 C++17 构建,并集成了 AVX2 SIMD 指令进行加速,旨在为 AI 工作负载提供极致的性能。
它特别适合与小型本地 GGUF 模型(嵌入和重排序模型,大小在24MB到636MB之间)以及长生命周期的 PHP 运行时(如 OpenSwoole、ReactPHP、RoadRunner 或 FrankenPHP)配合使用。在这些运行时中,工作进程是持久化的,可以一次性加载模型,之后每个请求都能在毫秒级延迟内完成完整的检索增强生成(RAG)流水线,且没有任何外部依赖。
快速开始
让我们通过几段代码,快速感受一下 MemVector 的能力。
首先是一个完整的语义搜索示例,这需要你在编译时启用 --with-llama 选项以集成本地模型支持:
// 使用本地嵌入模型进行语义搜索(需 --with-llama)
$emb = new MemVectorEmbedding('/models/all-MiniLM-L6-v2.Q8_0.gguf');
$store = new MemVectorStore(null, ['dimensions' => $emb->dimensions()]);
$store->set('php', $emb->embed('PHP is a server-side scripting language'), 'lang');
$store->set('python', $emb->embed('Python is used for machine learning'), 'lang');
$store->set('gravity', $emb->embed('Gravity pulls objects toward the earth'), 'science');
$store->set('dna', $emb->embed('DNA encodes genetic information'), 'science');
$results = $store->search($emb->embed('programming languages'), 2);
// [['key' => 'php', 'score' => 0.82, 'metadata' => 'lang'],
// ['key' => 'python', 'score' => 0.79, 'metadata' => 'lang']]
如果你想获得更精准的搜索结果,可以采用两阶段检索策略:先用向量搜索快速召回一批候选文档,再用交叉编码器进行精细的重排序:
// 二阶段检索:向量搜索 + 交叉编码器重排序(需 --with-llama)
$rr = new MemVectorReranker('/models/bge-reranker-v2-m3-Q8_0.gguf');
$candidates = $store->search($emb->embed('programming languages'), 50);
$results = $rr->rerank('programming languages', $candidates, 5);
当然,MemVector 也可以完全独立于本地模型工作。你可以使用来自 OpenAI 或其他任何来源的向量,它就是一个纯粹的、高性能的本地向量数据库:
// 或使用自定义向量(无需 llama.cpp)
$store = new MemVectorStore('/data/vectors', ['dimensions' => 1536]);
$store->set('doc_1', $openai_embedding, '{"title": "Introduction"}');
$results = $store->search($query_embedding, 10);
特性
MemVector 的设计目标是在提供丰富功能的同时,确保极致的性能和易用性。其主要特性包括:
- 键值 API:提供
set(key, vector)、get(key)、delete(key) 等直观操作,batchSet() 支持批量 upsert。
- 文本嵌入:可选集成 llama.cpp,支持直接在 PHP 中运行 GGUF 格式的嵌入模型进行推理。
- 交叉编码器重排序:支持完整的二阶段检索流水线——先快速向量搜索,再高精度重排序,显著提升结果相关性。
- 三种存储模式:灵活适应不同场景。
- 内存:临时存储,速度最快。
- 磁盘 (mmap):持久化存储,数据保存在文件中。
- 共享内存 (shm):支持跨 PHP 进程共享数据,适合常驻内存的PHP应用。
- HNSW 索引:搜索时自动构建业界流行的 HNSW (Hierarchical Navigable Small World) 图索引,支持高效的近似最近邻搜索。
- 向量量化:支持多种量化方式以减少内存占用,包括 F16、Int8(标量)、二值化以及乘积量化 (PQ)。
- 四种距离度量:支持余弦相似度、点积、欧氏距离和曼哈顿距离,满足不同算法需求。
- 无锁并发:通过
std::atomic 实现读写操作,在高并发场景下也能保持良好性能。
- AVX2 SIMD 加速:利用现代 CPU 的 SIMD 指令集,大幅加速余弦和点积距离的计算。
- 多段架构:采用固定容量的段式结构,可自动增长,管理海量向量时更高效。
- JSONL 导入导出:提供
dump() 和 load() 方法,方便数据的备份、迁移和与其他系统交互。
- 内存限制:可以设定 mmap 总内存使用量的上限,防止内存溢出。
性能
MemVector 的核心优势就在于其进程内的架构,消除了所有网络调用、序列化以及外部服务的开销。这意味着没有延迟,也没有额外成本。
嵌入生成
让我们对比一下生成文本嵌入的几种方式:
| 方法 |
单次延迟 |
成本 |
| OpenAI API (text-embedding-3-small) |
50-200 ms(网络往返) |
$0.02 / 1M tokens |
| Cohere API (embed-english-v3.0) |
50-200 ms(网络往返) |
$0.10 / 1M tokens |
| MemVector + 本地 GGUF 模型 |
5-15 ms(进程内) |
免费 |
本地嵌入比调用远程 API 快 10 到 40 倍,并且完全不存在按 token 计费的问题。模型通过 llama.cpp 在 PHP 进程内直接运行,没有 HTTP 开销、不需要 API 密钥,也不受速率限制。
向量搜索
在搜索环节,进程内向量数据库的优势更为明显:
| 方法 |
查询延迟 |
说明 |
| Pinecone / Qdrant / Weaviate(云) |
10-50 ms(网络) |
托管服务,按向量计费 |
| Pinecone / Qdrant / Weaviate(自托管) |
5-20 ms(网络) |
独立进程,仍有 TCP/gRPC 开销 |
| PostgreSQL + pgvector |
5-50 ms(查询 + 网络) |
共享数据库,有连接池开销 |
| MemVector(进程内) |
0.1-5 ms |
无网络、无序列化 |
MemVector 直接在 PHP 进程的内存(或 mmap 的文件)中搜索向量,省去了序列化、套接字通信和协议解析的步骤。结合 HNSW 索引和 AVX2 SIMD 加速,在百万级向量的规模下,大多数搜索都能在 1 毫秒内完成。
完整 RAG 流水线(嵌入 + 搜索 + 重排序)
对于一个完整的检索流程,将所有步骤放在进程内带来的收益是叠加的:
| 方法 |
总延迟 |
组件 |
| OpenAI 嵌入 + Pinecone 搜索 |
100-400 ms |
2 次网络往返 |
| OpenAI 嵌入 + Pinecone 搜索 + Cohere 重排序 |
200-600 ms |
3 次网络往返 |
| MemVector(全进程内) |
10-30 ms |
0 次网络往返 |
一个完整的两阶段RAG检索流水线,在单个 PHP 进程中即可闭环,没有任何外部依赖。
内存占用
MemVector 在内存使用上也做了大量优化,特别是模型的共享机制:
| 组件 |
RSS |
| MemVector 扩展(无模型) |
~1 MB |
| + 嵌入模型 (all-MiniLM-L6-v2, 24 MB GGUF) |
~33 MB |
| + 重排序模型 (bge-reranker-v2-m3, 636 MB GGUF) |
~200 MB |
| + 10 万向量 (384 维, f32) |
~150 MB |
| + 10 万向量 (384 维, int8 量化) |
~40 MB |
模型权重通过 mmap() 加载,并且可以跨多个 php-fpm 或 OpenSwoole 工作进程,通过操作系统的页面缓存进行共享,避免了重复加载的内存浪费。
要求
要使用 MemVector,你的环境需要满足以下条件:
- PHP 8.1+
- C++17 编译器(GCC 7+ 或 Clang 5+)
- (可选)llama.cpp,用于文本嵌入和交叉编码器重排序功能
构建
标准的 PHP 扩展构建流程:
phpize
./configure --enable-memvector
make
make test
安装 llama.cpp(可选,用于嵌入和重排序)
如果你需要文本嵌入和重排序功能,则需要先安装 llama.cpp。
macOS (Homebrew):
brew install llama.cpp
Linux(从源码构建):
git clone https://github.com/ggerganov/llama.cpp.git
cd llama.cpp
cmake -B build -DBUILD_SHARED_LIBS=ON -DGGML_CUDA=OFF
cmake --build build --config Release -j$(nproc)
sudo cmake --install build --prefix /usr/local
sudo ldconfig # 刷新共享库缓存
如果需要 GPU 加速,可以将 -DGGML_CUDA=OFF 替换为 -DGGML_CUDA=ON(前提是已安装 CUDA 工具包)。
构建支持 llama.cpp(嵌入和重排序)
在构建 MemVector 时,通过 --with-llama 参数指向你的 llama.cpp 安装路径:
phpize
./configure --enable-memvector --with-llama=/usr/local
make
make test
如果 llama.cpp 安装在其他目录,使用 --with-llama=DIR 指定即可。
下载模型
你需要下载 GGUF 格式的模型文件。以下是一些推荐模型:
嵌入模型(推荐 24 MB,384 维的 all-MiniLM-L6-v2):
curl -L -o all-MiniLM-L6-v2-Q8_0.gguf \
https://huggingface.co/leliuga/all-MiniLM-L6-v2-GGUF/resolve/main/all-MiniLM-L6-v2.Q8_0.gguf
| 模型 |
维度 |
大小 |
下载 |
| all-MiniLM-L6-v2 (Q8) |
384 |
24 MB |
HuggingFace |
| nomic-embed-text-v1.5 (Q8) |
768 |
138 MB |
HuggingFace |
| bge-small-en-v1.5 (F16) |
384 |
67 MB |
HuggingFace |
重排序模型(GGUF 格式的交叉编码器):
curl -L -o bge-reranker-v2-m3-Q8_0.gguf \
https://huggingface.co/gpustack/bge-reranker-v2-m3-GGUF/resolve/main/bge-reranker-v2-m3-Q8_0.gguf
| 模型 |
大小 |
下载 |
| bge-reranker-v2-m3 (Q8) |
636 MB |
HuggingFace |
| bge-reranker-v2-m3 (Q2) |
366 MB |
HuggingFace |
配置选项
在 ./configure 阶段,你可以使用以下标志:
| 标志 |
描述 |
--enable-memvector |
启用扩展(必需) |
--enable-memvector-avx2 |
强制启用 AVX2 SIMD(默认会自动检测) |
--with-llama[=DIR] |
启用 llama.cpp 支持(用于嵌入和重排序) |
功能检测
你可以在运行时通过检查是否定义了相关常量,来判断当前扩展支持哪些功能:
if (defined('MEMVECTOR_SHM')) {
// 共享内存模式可用
$store = new MemVectorStore('mystore', ['storage' => 'shm', 'dimensions' => 128]);
}
if (defined('MEMVECTOR_LLAMA')) {
// 编译时包含 llama.cpp 支持
$emb = new MemVectorEmbedding('/path/to/embedding-model.gguf');
$rr = new MemVectorReranker('/path/to/reranker-model.gguf');
}
PHP API
类:MemVectorStore
__construct(?string $dir = null, ?array $options = null)
创建或打开一个向量存储。
// 内存模式(临时存储,仅限单进程)
$store = new MemVectorStore();
$store = new MemVectorStore(null, ['dimensions' => 128]);
// 磁盘模式(持久化存储,基于 mmap 的文件)
$store = new MemVectorStore('/path/to/dir', ['dimensions' => 128]);
// 共享内存模式(持久化,支持跨进程访问)
$store = new MemVectorStore('mystore', ['storage' => 'shm', 'dimensions' => 128]);
选项(字符串值不区分大小写):
| 键 |
类型 |
默认值 |
描述 |
| storage |
string |
auto |
'disk'、'memory' 或 'shm' |
| dimensions |
int |
1536 |
向量维度(范围 1-4096) |
| distance |
string |
'cosine' |
'cosine'、'dot'、'euclidean'、'manhattan' |
| quantization |
string |
'none' |
'none'、'f16'、'int8'、'binary'、'pq' |
| segment_size |
int |
10M(磁盘/shm)、4096(内存) |
每个段的最大记录数量 |
| writable |
bool |
true |
是否为只读模式(主要用于 shm 模式) |
| memory_limit |
int / string |
0 |
mmap 总内存使用限制,0 表示无限制 |
按键插入或替换向量(upsert 语义)。如果键已存在,则更新对应的向量和元数据。
$store->set('doc_42', $vector);
$store->set('doc_42', $new_vector, '{"category": "science"}'); // 替换前一个向量
$key:唯一的字符串键,最大长度 63 个字符。
$vector:浮点数数组,必须与存储初始化时设定的维度相匹配。
$metadata:可选的元数据字符串,最大长度 4096 字节,通常用于存储 JSON。
batchSet(array $batch): int
一次性插入或替换多个向量。返回成功插入(或更新)的项目数量。这对于数据初始化或批量更新非常高效。
$count = $store->batchSet([
['key' => 'doc_1', 'vector' => $vec1],
['key' => 'doc_2', 'vector' => $vec2, 'metadata' => '{"tag": "a"}']
]);
总的来说,MemVector 为 PHP 生态带来了一个前所未有的高性能、低成本、零依赖的本地 AI 基础设施解决方案。它尤其适合那些对延迟有严苛要求、希望简化架构、或需要在受限环境中部署 RAG 应用的场景。如果你正在为 PHP 项目寻找 AI 能力的内化方案,不妨到云栈社区的技术论坛深入探讨,或在我们的资源库中寻找更多相关的工具和灵感。