本文目录导读:

- 方案一:使用 PostgreSQL + pgvector 扩展(推荐,生产可靠)
- 方案二:使用独立的 Vector Database 客户端(如 Qdrant、Milvus)
- 方案三:使用 SQLite + sqlite-vec 扩展(适合轻量级/单机)
- 方案四:使用 Redis Stack 的向量搜索模块
- 总结与选型建议
在 PHP 项目中实现向量数据库,通常有 3 种主流方式,由于 PHP 本身没有原生的向量索引库,需要借助扩展、外部服务或数据库中间件。
以下是具体的实现路径和代码示例:
使用 PostgreSQL + pgvector 扩展(推荐,生产可靠)
这是目前 PHP 与向量数据库结合最成熟、最稳定的方式,PostgreSQL 加上 pgvector 扩展后,支持存储向量和进行相似性搜索。
环境准备
- 安装 PostgreSQL 并启用
pgvector扩展。 - 安装 PHP 的
pgsql或PDO_PGSQL扩展。
创建表和索引
-- 启用扩展
CREATE EXTENSION vector;
-- 创建表,vec 字段定义为 768 维向量(OpenAI 等模型常用)
CREATE TABLE items (
id SERIAL PRIMARY KEY,
content TEXT,
embedding vector(768)
);
-- 创建 IVFFlat 索引(加速搜索,列表数一般为 行数/1000)
CREATE INDEX ON items USING ivfflat (embedding vector_cosine_ops) WITH (lists = 100);
PHP 代码实现
<?php
// 连接数据库
$pdo = new PDO('pgsql:host=localhost;dbname=testdb', 'user', 'pass');
// 1. 插入向量(假设已有嵌入模型返回的数组)
function insertEmbedding(string $text, array $embedding): void
{
global $pdo;
$vecStr = '[' . implode(',', $embedding) . ']'; // pgvector 格式
$stmt = $pdo->prepare('INSERT INTO items (content, embedding) VALUES (?, ?::vector)');
$stmt->execute([$text, $vecStr]);
}
// 2. 查询相似内容(余弦距离)
function searchSimilar(array $queryEmbedding, int $topK = 5): array
{
global $pdo;
$vecStr = '[' . implode(',', $queryEmbedding) . ']';
// 使用 <=> 余弦距离运算符
$stmt = $pdo->prepare('SELECT content, 1 - (embedding <=> ?::vector) AS similarity
FROM items
ORDER BY embedding <=> ?::vector
LIMIT ?');
$stmt->execute([$vecStr, $vecStr, $topK]);
return $stmt->fetchAll(PDO::FETCH_ASSOC);
}
// 示例用法
$embedding = [0.1, 0.2, 0.3, ...]; // 768 维
insertEmbedding('hello world', $embedding);
$results = searchSimilar([0.11, 0.21, 0.31, ...], 5);
优点:标准 SQL 语法、支持事务、与现有数据无缝集成。
缺点:需要 PostgreSQL 安装 pgvector 扩展(云服务可能有支持限制)。
使用独立的 Vector Database 客户端(如 Qdrant、Milvus)
这些专用向量数据库提供 RESTful 或 gRPC 接口,PHP 通过 HTTP 客户端调用。
安装 Qdrant(推荐轻量级)
docker run -p 6333:6333 qdrant/qdrant
安装 PHP HTTP 客户端
composer require guzzlehttp/guzzle
PHP 代码示例
<?php
require 'vendor/autoload.php';
use GuzzleHttp\Client;
class QdrantVectorDB
{
private Client $client;
private string $collection = 'my_collection';
public function __construct()
{
$this->client = new Client(['base_uri' => 'http://localhost:6333']);
}
// 创建集合(一次)
public function createCollection(): void
{
$body = [
'vectors' => ['size' => 768, 'distance' => 'Cosine']
];
$this->client->put("/collections/{$this->collection}", [
'json' => $body
]);
}
// 插入点
public function upsert(int $id, array $vector, array $payload = []): void
{
$this->client->put("/collections/{$this->collection}/points", [
'json' => [
'points' => [[
'id' => $id,
'vector' => $vector,
'payload' => $payload
]]
]
]);
}
// 搜索
public function search(array $queryVector, int $limit = 5): array
{
$response = $this->client->post("/collections/{$this->collection}/points/search", [
'json' => [
'vector' => $queryVector,
'limit' => $limit
]
]);
return json_decode($response->getBody(), true);
}
}
// 使用
$db = new QdrantVectorDB();
$db->createCollection();
$db->upsert(1, [0.1, 0.2, ...], ['content' => 'some text']);
$result = $db->search([0.11, 0.21, ...]);
优点:性能极佳、支持大规模分布式、原生向量索引(HNSW)。
缺点:需要额外维护一个数据库服务,数据与关系型数据库分离。
使用 SQLite + sqlite-vec 扩展(适合轻量级/单机)
如果项目是单机或小型应用,sqlite-vec 是 PHP 可直接集成的向量 SQLite 扩展。
安装 sqlite-vec
# 下载对应平台的 .so/.dll 文件,或通过 composer 加载 composer require aphp/sqlite-vec
PHP 代码
<?php
// 注意:需要加载扩展,不同环境方式不同
$db = new SQLite3('vectors.db');
$db->enableLoadingExtensions();
$db->loadExtension('sqlite_vec.so'); // 动态加载
// 创建虚拟表
$db->exec("CREATE VIRTUAL TABLE IF NOT EXISTS vec_memory USING vec0(
content VECTOR[768]
)");
// 插入
$stmt = $db->prepare("INSERT INTO vec_memory(rowid, content) VALUES (?, ?)");
$stmt->bindValue(1, 1, SQLITE3_INTEGER);
$stmt->bindValue(2, json_encode([0.1, 0.2, ...]), SQLITE3_TEXT);
$stmt->execute();
// 搜索
$queryVec = json_encode([0.11, 0.21, ...]);
$res = $db->query("SELECT rowid, distance
FROM vec_memory
WHERE content MATCH ?
ORDER BY distance
LIMIT 5", $queryVec);
优点:无外部依赖,PHP 进程内运行。
缺点:性能上限低、不适合大数据量(>10万条)、并发差。
使用 Redis Stack 的向量搜索模块
如果项目已经在用 Redis,可以直接启用 redis-stack-server。
启动 Redis Stack
docker run -p 6379:6379 redis/redis-stack-server
PHP 代码
<?php
$redis = new Redis();
$redis->connect('127.0.0.1', 6379);
// 创建索引(FT.CREATE)
$redis->rawCommand('FT.CREATE', 'idx:vectors', 'ON', 'HASH', 'PREFIX', '1', 'doc:',
'SCHEMA', 'title', 'TEXT', 'WEIGHT', '1.0',
'embedding', 'VECTOR', 'FLAT', '6', 'TYPE', 'FLOAT32', 'DIM', '768', 'DISTANCE_METRIC', 'COSINE');
// 插入
$redis->hSet('doc:1', 'embedding', pack('f*', ...$embeddingArray));
// 搜索(FT.SEARCH)
$query = "*=>[KNN 5 @embedding \$vec AS score]";
$params = ['vec' => pack('f*', ...$queryEmbedding)];
$result = $redis->rawCommand('FT.SEARCH', 'idx:vectors', $query, 'PARAMS', '2', 'vec', $params['vec'], 'SORTBY', 'score', 'LIMIT', '0', '5');
优点:无需新服务、速度快、支持混合搜索。
缺点:Redis 内存消耗大、持久化机制不如专业数据库。
总结与选型建议
| 场景 | 推荐方案 |
|---|---|
| 已有 PostgreSQL,数据强一致 | pgvector(方案一) |
| 要高并发、海量向量(>100万) | Qdrant/Milvus(方案二) |
| 单机小应用、不想搭服务 | sqlite-vec(方案三) |
| 已用 Redis,内存够用 | Redis Stack(方案四) |
关键点提醒:
- 向量生成:PHP 通常需要调用外部 API(如 OpenAI、本地 Python 服务)来生成向量,因为 PHP 没有好用的本地嵌入模型库。
- 批量插入:向量数据库建议用事务或批量 API 插入,单条插入性能差。
- 索引调优:
pgvector的 IVFFlat 需要调lists参数,Qdrant的 HNSW 需要调ef_construct,否则精确度下降。
建议初学者从 方案二(Qdrant + Guzzle) 开始,逻辑清晰且文档丰富。