seekdb 冒烟与混合检索实验
用一条固定流程,把「seekdb 装好了没、索引能不能用、两路检索怎么合并」这三件事一次验证完。
流程是幂等的:只操作独立实验库 seekdb_smoke_lab,跑完删除,不碰任何业务库。
什么时候用
- 刚部署完 seekdb,想确认能连、版本对、建表建索引都不报错。
- 写混合检索(向量 + 全文)代码前,先用最小样例确认语法和数据形态。
- 检索结果「看起来不对」时,用它把召回、距离值、命中词逐条打出来对照。
前置条件
| 项 | 默认值 | 覆盖方式 |
|---|---|---|
| 地址 | 127.0.0.1 | SEEKDB_HOST |
| 端口 | 2881 | SEEKDB_PORT |
| 用户 | root | SEEKDB_USER |
| 密码 | 空 | SEEKDB_PASSWORD |
| 客户端 | mysql | SEEKDB_CLIENT(也可填 obclient) |
先确认客户端存在:command -v mysql obclient。没有客户端时不要硬跑,先装客户端
(apt install mysql-client / dnf install mysql),因为 seekdb 走 MySQL 协议,这一步能省掉很多猜测。
步骤
第 1 步:连通性与版本
mysql -h"${SEEKDB_HOST:-127.0.0.1}" -P"${SEEKDB_PORT:-2881}" -u"${SEEKDB_USER:-root}" \
-e "select 1 as ok, version() as ver, @@port as port;"
判据:返回 ok=1,ver 形如 5.7.25-OceanBase seekdb-vX.Y.Z。
ver 里的 seekdb-v 段就是 seekdb 自己的版本,和 OceanBase 兼容版本是两件事,报告里要分开写。
连不上时按这个顺序查,不要直接改配置:
- 进程在不在:
ps -ef | grep -c [s]eekdb;systemd 部署再看systemctl status seekdb。 - 端口在不在听:
ss -lntp | grep 2881。 - 是不是只听 unix socket:检查
etc/observer.config.bin或安装目录下的etc/*.conf里mysql_port/local_ip。 - 报
Access denied说明服务活着,是账号问题,不是部署问题——这两类错误不要混着报。
第 2 步:跑冒烟脚本
bash scripts/smoke.sh
脚本会依次执行下面 4 组断言,任何一组失败就停下并打印失败的那条 SQL:
| 组 | 断言 |
|---|---|
| A 建表 | VECTOR(4) 列、FULLTEXT 列、普通二级索引都能建 |
| B 建索引 | CREATE VECTOR INDEX ... WITH (distance=l2, type=hnsw, lib=vsag, ...) 成功 |
| C 向量检索 | ORDER BY l2_distance(...) APPROXIMATE LIMIT 3 返回 3 行且首行是自身 |
| D 混合检索 | 一条 SQL 用 UNION 合并向量路与全文路候选,两路都要出现 |
脚本对每一步单独判定,失败时打印的是该步的原始 SQL,可直接粘去手动复现。
第 3 步:读结果
脚本先打印每组的耗时,再打印每条命中的 id、距离、命中的文本。先看结果集对不对,再看耗时——
耗时在小数据量下没有意义,20 行的表测不出索引性能差异,别拿它下性能结论。
核心 SQL(可直接抄)
建表、建索引、三种查询的完整写法见 references/sql-cheatsheet.md。要点:
CREATE TABLE lab_docs (
id INT PRIMARY KEY,
title VARCHAR(128),
body TEXT,
embedding VECTOR(4),
FULLTEXT INDEX ft_body (body) WITH PARSER SPACE
);
CREATE VECTOR INDEX vidx_embedding ON lab_docs(embedding)
WITH (distance=l2, type=hnsw, lib=vsag, m=16, ef_construction=128);
-- 向量路
SELECT id, l2_distance(embedding, '[0.1,0.2,0.3,0.4]') AS d
FROM lab_docs ORDER BY d APPROXIMATE LIMIT 3;
-- 全文路
SELECT id FROM lab_docs WHERE MATCH(body) AGAINST('oceanbase') LIMIT 3;
WITH PARSER SPACE 是按空格切词,英文语料够用;中文语料必须换分词器,否则整句会被当成一个词,
表现为「明明有这个词却召回 0 条」。这一点是最常见的误判来源,排查时先怀疑分词器,别怀疑索引没建。
常见坑
| 现象 | 真实原因 | 处理 |
|---|---|---|
| 建 HNSW 索引报 lib 不支持 | 该版本没编 vsag 库 | 去掉 lib=vsag 用默认,或按版本说明装依赖 |
| APPROXIMATE 报语法错 | 写成了 APPROXIMATE TRUE 或放在 ORDER BY 之外 | 只跟在 ORDER BY ... LIMIT 这条路径上 |
| 向量检索返回行数不足 | HNSW 是近似的,候选不足时不会自动回退 | 加大 ef_search,或先把过滤条件放宽 |
| 全文召回 0 条但数据里有 | 分词器与语料语言不匹配 | 换 PARSER,或先用 MATCH 测单个英文单词确认索引生效 |
| 混合检索只返回向量命中、一条关键词命中都没有,且不报错 | UNION 两个分支没各自加括号,后面的 LIMIT 被当成整个 UNION 的 LIMIT | 两个分支都加括号,见 references/sql-cheatsheet.md |
| 同一条件 ANN 与精确查询延迟差几十倍 | 向量路径的 filter 未走化简,谓词全部逐行求值 | 见下方「进阶」 |
进阶:为什么带过滤条件的 ANN 会突然变慢
带 WHERE 的 ANN 查询会走 VECTOR INDEX ADAPTIVE SCAN (PRE-FILTER)。已知问题是:这条路径把谓词
原样留在逐行 filter 里,不做常量折叠与冗余比较合并。所以 age < 10 AND age < 20 AND age < 30
会在每行被求值 3 次,而普通 range scan 里同样的条件只在区间裁剪阶段算一次。谓词越多,放大越明显;
精确查询不受影响。用 EXPLAIN 看 filter 是否被化简,是最快的判据。
收尾
脚本结束时会 DROP DATABASE seekdb_smoke_lab。如果中途失败留下残库,手动删:
DROP DATABASE IF EXISTS seekdb_smoke_lab;
汇报时把「连通性 / 建表建索引 / 向量路 / 全文路 / 混合路」五项分开写通过或不通过, 不要合并成一句「全部正常」——单独一项失败往往就是后面写业务代码时踩的坑。
<!-- OB-S:28c_pEe3zJonMCkED4PbSw -->
Scan to join WeChat group