OceanBase seekdb 混合检索实战助手
给 AI 助手(或人)一份逐条实测过的 seekdb 检索速查表:语法怎么写、为什么查不到、性能怎么取舍。
本 Skill 的每条语法、每个报错、每个召回差异都在
OceanBase 4.3.5.3 seekdb (r1.4.0.0)(5.7.25-OceanBase seekdb-v1.4.0.0)上实跑验证过。
踩过的坑都写在这里,不要再重复踩。
何时使用
- 要在 seekdb / OceanBase 上建全文索引或向量索引,写
MATCH ... AGAINST、l2_distance、cosine_distance; - 要做混合检索(全文召回 + 向量排序 + 结构化过滤),或给 RAG 应用选检索方案;
- 排查:中文查不到、布尔模式查不到、建表报 1235、向量维度报错、索引写了数据却检索不到、召回率上不去;
- 给中文/英文/混合语料选分词器,或给领域专有名词配自定义词典。
第 0 步:先确认环境(不要跳过)
SELECT version();
SHOW VARIABLES LIKE 'version_comment';
期望看到 version_comment 里含 seekdb。如果不是 seekdb,
下面 FULLTEXT 与 VECTOR INDEX 的语法可能不同(社区版 OceanBase 的向量索引语法不一样)。
环境体检用随附脚本(只读,不写任何数据):
export OB_HOST=127.0.0.1 OB_PORT=2881 OB_USER=root OB_PASSWORD='***'
python3 ob_check.py --db your_db --table your_table
脚本随 Skill 一起分发,位于 Skill 根目录(仓库副本中的路径为 scripts/ob_check.py)。
输出包含:版本、列定义、全文索引及实际分词器、向量索引、向量列维度、IK 词典表,
并检查常见问题(中文语料配 space 分词器、有向量列但没建向量索引等)。
加 --json 可输出结构化结果。退出码 0 = 无阻塞问题,1 = 有阻塞问题。
第 1 条线:全文检索
建表
CREATE TABLE kb (
id bigint PRIMARY KEY,
doc_id varchar(64),
title varchar(512),
content varchar(2048),
FULLTEXT INDEX ft_title(title, content)
);
FULLTEXT INDEX是独立于普通INDEX的索引类型,可以建多列(FULLTEXT INDEX f(col1, col2));- ❌
INDEX idx(title) fulltext是语法错误(ERROR 1064); - ⚠️ 不写
WITH PARSER时默认分词器是space,中文直接废掉,见第 4 条线。
查询
-- 自然语言模式
SELECT doc_id, title FROM kb WHERE MATCH(title, content) AGAINST('OceanBase');
-- 布尔模式:+ 必须包含,- 必须不包含
SELECT doc_id, title FROM kb WHERE MATCH(title, content) AGAINST('+OceanBase -MySQL' IN BOOLEAN MODE);
MATCH(...) 里的列必须和建索引时的列一致(建了 (title, content) 就写两个)。
不一致报 ERROR 1191 (HY000): Can't find FULLTEXT index matching the column list。
第 2 条线:向量检索
建表:向量索引有独立语法
CREATE TABLE kb (
id bigint PRIMARY KEY,
vec VECTOR(4),
VECTOR INDEX idx_vec(vec) WITH (distance=l2, type=hnsw)
);
这是最容易写错的地方,实测结论:
| 写法 | 结果 |
| --- | --- |
| INDEX idx_vec(vec) | ❌ ERROR 1235: create index on vector column is not supported |
| ALTER TABLE kb ADD INDEX idx_vec(vec) | ❌ 同上 |
| VECTOR INDEX idx_vec(vec) | ❌ ERROR 1235: vector index params not set is not supported |
| VECTOR INDEX idx_vec(vec) WITH (distance=l2) | ❌ ERROR 1235: vector index params not set distance and type is not supported |
| VECTOR INDEX idx_vec(vec) WITH (distance=l2, type=hnsw) | ✅ 正确 |
也就是说 distance 和 type 必须同时给。type 目前只支持 hnsw(type=flat 报
ERROR 1235: this type of vector index type is not supported)。
建好之后 SHOW CREATE TABLE 会显示默认参数:
VECTOR KEY `idx_vec` (`vec`) WITH (DISTANCE=L2, TYPE=HNSW, LIB=VSAG, M=16, EF_CONSTRUCTION=200, EF_SEARCH=64)
查询
-- 近似检索(走 HNSW 索引,快)
SELECT id FROM kb ORDER BY l2_distance(vec, [1,0,0,0]) APPROXIMATE LIMIT 10;
-- 精确检索(暴力算,慢但准,用于算 Recall@K 的真值)
SELECT id FROM kb ORDER BY l2_distance(vec, [1,0,0,0]) LIMIT 10;
-- 余弦距离
SELECT id, cosine_distance(vec, [1,0,0,0]) AS d FROM kb ORDER BY d LIMIT 10;
APPROXIMATE 是开关,不是修饰词:加它才走索引,不加就是全表精确算。两者结果可能不同
(HNSW 是近似召回),这是正常的——但如果你要做 Recall@K 校验,真值必须用不带 APPROXIMATE 的精确查询。
第 3 条线:混合检索(一条 SQL 干完)
SELECT doc_id,
title,
l2_distance(vec, [1,0,0,0]) AS dist
FROM kb
WHERE MATCH(title, content) AGAINST('+检索' IN BOOLEAN MODE)
AND tenant_id = 42 -- 结构化过滤条件放在同一层
ORDER BY dist
LIMIT 10;
要点:
- 过滤、召回、排序在同一个 SELECT 里,不要在客户端把两路结果拼起来——那会让过滤条件失效、 召回率不可控,也失去数据库一次查询完成全部工作的优势;
ORDER BY <距离函数>必须有对应的VECTOR INDEX兜底,否则退化成全表算;- 向量维度必须与
VECTOR(n)完全一致,写错维度直接报错,不要"截断"或"补零"绕过。
更多可复制的 SQL 模板见同目录的 hybrid-search-sql.md(仓库副本中为 references/hybrid-search-sql.md)。
第 4 条线:中文检索(最容易踩的坑)
坑 1:默认分词器是 space,中文召回全空
space 按空白切分。中文文本没有空格,等于一个超长 token,所以:
-- 默认 space 分词器 + 中文:召回为空(实测 0 行)
SELECT id FROM kb WHERE MATCH(title, content) AGAINST('混合检索');
解决办法是显式指定分词器:
FULLTEXT INDEX ft(title, content) WITH PARSER ik -- 中文首选,支持自定义词典
FULLTEXT INDEX ft(title, content) WITH PARSER ngram -- n-gram 切分
FULLTEXT INDEX ft(title, content) WITH PARSER ngram2 -- 另一种 n-gram 切分
坑 2:布尔模式下,多字中文词召回为空
实测结论(ik / ngram / ngram2 三种分词器都一样):
| 查询 | 自然语言模式 | 布尔模式 |
| --- | --- | --- |
| AGAINST('混合检索') | ✅ 命中 | — |
| AGAINST('混合检索' IN BOOLEAN MODE) | — | ❌ 0 行 |
| AGAINST('混合检' IN BOOLEAN MODE) | — | ❌ 0 行 |
| AGAINST('混合' IN BOOLEAN MODE) | — | ✅ 命中(2 字词) |
| AGAINST('检索' IN BOOLEAN MODE) | — | ✅ 命中(2 字词) |
| AGAINST('+混合' IN BOOLEAN_MODE) | — | ✅ 命中 |
| AGAINST('"混合检索"' IN BOOLEAN MODE) | — | ❌ 0 行(引号短语也不行) |
结论:中文查询不要用 BOOLEAN 模式。 布尔模式对中文按 2 字粒度匹配, 3 字及以上的词查不到。中文走自然语言模式,英文走布尔模式,两者分开写。
坑 3:各分词器的参数名不一样
| 分词器 | 参数 | 可用值 |
| --- | --- | --- |
| space(默认) | min_token_size, max_token_size | 默认 3 / 84 |
| ik | ik_mode | smart(默认)/ max_word 等 |
| ngram | ngram_token_size | 如 2 |
| ngram2 | min_ngram_size, max_ngram_size | 如 2 / 3 |
⚠️ 给 ngram 传 min_ngram_size/max_ngram_size 会报 ERROR 1235 ngram config not supported
(这两个是 ngram2 的参数名);给 ngram2 传 ngram_token_size 同理不生效。
坑 4:自定义词典——语法接受,但实测不生效
WITH PARSER ik 支持 dict_table 属性,DDL 不会报错:
-- 词典表:放在 oceanbase 库里,带 word 列
CREATE TABLE oceanbase.my_dict (
word varchar(100) PRIMARY KEY
);
INSERT INTO oceanbase.my_dict (word) VALUES ('OceanBase'), ('seekdb'), ('向量数据库');
-- 挂到全文索引上
ALTER TABLE kb ADD FULLTEXT INDEX ft(title, content)
WITH PARSER ik PARSER_PROPERTIES=(dict_table='my_dict');
但 A/B 实测:加了自定义词典与不加,分词结果完全一致(构造长复合词
向量数据库检索系统 做对照,数据库/架构 的命中情况两边相同)。
往内置词典表 oceanbase.__ft_dict_ik_utf8 灌词后同样没有变化。
本版本的内置词典表本身也是空的(0 行)。
结论:seekdb r1.4.0.0 上不要指望用词典控制分词。
需要可控的切分粒度时,用 ngram(固定 ngram_token_size)或 ngram2
(min_ngram_size / max_ngram_size)——这两个分词器的粒度是显式可控且实测有效的。
另外几个与词典相关的版本差异(实测于 r1.4.0.0):
- ❌
CREATE TABLE ... FULLTEXT_DICT='Y'→ERROR 1064,seekdb 没有这个表级属性; - ❌
ALTER SYSTEM REFRESH FULLTEXT DICT ...→ERROR 1064,seekdb 没有这条语句; - ✅ 词典表读取方式是
SELECT word FROM oceanbase.<表名> ORDER BY word, 所以表名不要带库名前缀(带db.前缀会拼成非法的三段式),表要建在oceanbase库里。
排障速查
| 现象 | 原因 | 处理 |
| --- | --- | --- |
| 中文 AGAINST 召回为空 | 默认 space 分词器不切中文 | 改用 WITH PARSER ik / ngram / ngram2 |
| 中文 + BOOLEAN 模式召回为空 | 布尔模式按 2 字粒度匹配中文 | 中文改用自然语言模式 |
| ERROR 1191 | MATCH 的列与索引列不一致 | 与建索引的列逐一对齐 |
| ERROR 1235 ngram config not supported | ngram 用了 ngram2 的参数名 | ngram 用 ngram_token_size,ngram2 用 min/max_ngram_size |
| ERROR 1235 create index on vector column is not supported | 用普通 INDEX 给向量列建索引 | 改用 VECTOR INDEX ... WITH (distance=l2, type=hnsw) |
| ERROR 1235 vector index params not set | VECTOR INDEX 缺参数 | distance 和 type 必须同时给 |
| 向量维度报错 | 写入维度 ≠ VECTOR(n) | 对齐维度,不要截断补零 |
| 写入后检索不到新数据 | 增量索引尚未刷新 | 等待刷新或 call dbms_vector.rebuild_index('idx','tbl','col'); |
| 精确与近似结果不一致 | APPROXIMATE 走 HNSW 近似召回 | 正常现象;用精确查询算真值校验 Recall@K |
| 召回率低 | 只有向量召回 / 只有全文召回 | 混合检索;或给中文换分词器、加词典 |
| 查询很慢 | ORDER BY 的距离函数没走索引 | 确认 VECTOR INDEX 存在;检查是否漏写 APPROXIMATE |
完整报错对照与更多场景见同目录的 troubleshooting.md(仓库副本中为 references/troubleshooting.md)。
性能与质量的取舍
- 近似 vs 精确:
APPROXIMATE走 HNSW,延迟低;精确查询全表算距离,只用于小数据集算真值; - 过滤与召回的顺序:结构化过滤条件越有选择性(租户、状态、时间窗),向量检索越快;
- 召回质量的验证方式(不要只看展示):
- 用同一批查询分别跑近似与精确查询;
- 以精确查询结果为真值,算
Recall@K = |近似TopK ∩ 精确TopK| / K; - 同一口径下比较"量化前/后""换分词器前/后"的质量变化;
- 分词器直接影响召回:默认
space对中文等于没分词,换成ngram/ik后召回通常会显著变化, 换完必须重新测 Recall@K,而不是凭感觉; - 查询模式也要测:同一份语料,自然语言模式和布尔模式的召回结果可以完全不同, 上线前两种模式都跑一遍 Recall@K。
边界
- 本 Skill 的语法基于 seekdb
r1.4.0.0;更低版本或社区版 OceanBase 的全文/向量索引能力可能不同, 遇到语法错误先看show create table; - 不负责模型调用与 Embedding 生成,只负责存储与检索这一层;
- 不包含任何账号、密码、连接串。连接信息一律从环境变量读取。
参考
hybrid-search-sql.md—— 可直接复制的建表、召回、混合检索、RRF 融合、Recall@K 校验 SQL(仓库副本中为references/hybrid-search-sql.md)troubleshooting.md—— 报错与"查不到"问题的完整对照表(仓库副本中为references/troubleshooting.md)ob_check.py—— 只读体检脚本,输出索引与分词器现状(仓库副本中为scripts/ob_check.py)validate_skill_sql.sql—— 本文档全部 SQL 的可执行验证脚本,逐条断言(仓库副本中为scripts/validate_skill_sql.sql)- OceanBase seekdb 项目:https://github.com/oceanbase/seekdb
微信扫一扫