体验全文索引
全文索引介绍
全文索引(Full-Text Index)是数据库中用于加速文本数据搜索的特殊索引类型,特别适用于处理包含大量文本字段(如文章内容、评论、博客等)的查询需求。它支持快速的关键词匹配查询,可以在文本中查找一个或多个词语,并返回相关结果。全文索引常常应用于搜索引擎和文本分析系统中,有效帮助企业快速定位关键信息,提升搜索效率。
在企业的实际生产中,全文索引功能可以应用于系统日志分析、用户分析等众多场景,全文索引能够对数据做到高效率过滤筛选、或是高质量相关性评估。在 AI 领域,seekdb 基于稀疏稠密向量与全文索引相结合的多路召回架构,能在具有特殊知识领域的 RAG 系统中实现更高效、更精准的召回效果。
全文索引的主要功能包括:
-
全文搜索:通过构建全文索引,能够对整个文档或大段文本内容进行全面的索引,从而实现更灵活、高效的搜索。
-
快速查找:用户可以根据输入的关键词在数据库中快速查找匹配的文本,大大减少了搜索时间。
-
高效处理大量文本:全文索引能够有效处理各种类型的文本数据,包括文章、报告、网页和电子邮件等,为用户提供精准且快速的搜索体验。
-
支持复杂查询:除了基础的关键词搜索,全文索引还能支持复杂的查询需求,例如近似匹配和相关性排序,极大地丰富了数据库的搜索能力。
通过引入全文索引功能,seekdb 可以在面对大规模文本数据和复杂搜索需求时,显著提高查询性能,使得用户能够更高效地获取所需信息。
使用全文索引实现海量新闻数据的高效搜索
以下示例以新闻资讯类业务场景为例,演示通过使用全文索引功能如何在海量信息中快速找到目标新闻。我们将通过查询中的关键词,展示 seekdb 全文索引在功能、性能和易用性方面的提升。
前提条件
要顺利操作并体验 seekdb 的全文索引功能,请确保满足以下前提条件:
-
环境要求:您已部署 seekdb。
-
数据库创建:确保您已创建数据库。如需了解详细步骤,请参见 创建数据库。
操作步骤
以下操作步骤将引导您体验 seekdb 的全文索引及常用视图和查询技巧。
步骤一:导入数据集
seekdb 内置了支持中文的 IK 分词器,以及相较于传统自然语言处理更为高效的布尔模式。我们将使用 中文足球体育新闻数据集 导入数据到 seekdb 中,创建一张名为 sport_data_whole 的无主键分区表,包含三列变长字符(event、date、news),并使用 IK 中文分词器对 news 字段建立全文索引,指定其为 max_word 模式。
IK 分词器的 smart 模式与 max_word 模式的区别在于,前者在匹配到最长词语后不会继续匹配更短的词语。
seekdb 内置的分词器还包括适合英文的 space 和 beng。以及按照字符长度分割的 ngram。
-- 创建表并使用 IK 分词器进行全文索引
CREATE TABLE sport_data_whole (
event VARCHAR(64),
date VARCHAR(16),
news VARCHAR(65535),
FULLTEXT INDEX ft_idx1_news(news)
WITH PARSER ik PARSER_PROPERTIES = (ik_mode = "max_word")
);
通过客户端本地文件的方式,将新闻数据集导入到表格内,预计时间为 6.5 秒左右。
-- 导入数据
LOAD DATA /*+ PARALLEL(8) */ LOCAL INFILE '/home/admin/sports_data_whole.csv' INTO TABLE sport_data_whole
FIELDS TERMINATED BY ',' LINES TERMINATED BY '\n';
导入数据后,表中共计 100000 条新闻,平均新闻长度约为 2700 个汉字,原始数据的大小约为 59MB。经过高效压缩后,实际存储的总空间不足 30MB,其中较大的部分为全文索引中的倒排和正排辅助表,存储了大量的分词记录。
-- 验证导入的条数
SELECT AVG(LENGTH(news)), COUNT(*) FROM sport_data_whole;
返回结果如下所示:
+-------------------+----------+
| AVG(LENGTH(news)) | COUNT(*) |
+-------------------+----------+
| 58.9944 | 100000 |
+-------------------+----------+
1 row in set
-- 查询视图,验证结果
SELECT * FROM oceanbase.DBA_OB_TABLE_SPACE_USAGE WHERE DATABASE_NAME = 'test';
返回结果如下所示:
+----------+---------------+----------------------------------------+-------------+---------------+
| TABLE_ID | DATABASE_NAME | TABLE_NAME | OCCUPY_SIZE | REQUIRED_SIZE |
+----------+---------------+----------------------------------------+-------------+---------------+
| 500083 | test | sport_data_whole | 3774028 | 4198400 |
| 500084 | test | __idx_500083_ft_idx1_news | 11733278 | 14684160 |
| 500085 | test | __idx_500083_ft_idx1_news_fts_doc_word | 11074714 | 14684160 |
+----------+---------------+----------------------------------------+-------------+---------------+
步骤二:利用全文索引查询
使用已存储的新闻数据集和索引,我们可以进行多条件组合或高过滤性的搜索。例如,作为一名球迷,如果我想搜索包含“拜仁”和“乌龙球”的新闻,可以使用布尔模式。
相较于没有索引的字符串 LIKE 匹配,布尔模式的语法更为简洁,查询速度也更快。
-- 使用布尔模式进行查询,查找同时包含“乌龙球”和“拜仁”的新闻
SELECT COUNT(*) FROM sport_data_whole
WHERE MATCH (news) AGAINST ('+乌龙球 +拜仁' IN BOOLEAN MODE);
返回结果为:
+----------+
| COUNT(*) |
+----------+
| 14645 |
+----------+
1 row in set (0.03 sec)
对比之下,使用 LIKE 查询的方式:
-- 使用 LIKE 语法进行查询
SELECT COUNT(*) FROM sport_data_whole
WHERE news LIKE '%乌龙球%' AND news LIKE '%拜仁%';
返回的结果也为:
+----------+
| COUNT(*) |
+----------+
| 14645 |
+----------+
1 row in set (0.05 sec)
关于返回的两条新闻,我们可以进一步进行排名(ranking),通过输出结果中的分值来判断哪条新闻与查询更相关。
-- 返回新闻的事件、日期及分值,以帮助判断相关性
SELECT event, date, MATCH (news) AGAINST ('乌龙球 拜仁') AS score
FROM sport_data_whole
WHERE MATCH (news) AGAINST ('+乌龙球 +拜仁' IN BOOLEAN MODE) limit 10;
返回结果如下:
+-----------+------+--------------------+
| event | date | score |
+-----------+------+--------------------+
| 网球赛 | 0626 | 1.3530436897138691 |
| 足球赛 | 0926 | 1.3530436897138691 |
| 篮球赛 | 0527 | 1.3530436897138691 |
| 篮球赛 | 0415 | 1.3530436897138691 |
| 奥运会 | 1104 | 1.3530436897138691 |
| 世界杯 | 1019 | 1.3530436897138691 |
| 网球赛 | 0901 | 1.3530436897138691 |
| 网球赛 | 0731 | 1.3530436897138691 |
| 奥运会 | 0605 | 1.3530436897138691 |
| 奥运会 | 0907 | 1.3530436897138691 |
+-----------+------+--------------------+
10 rows in set
同时,布尔模式还允许我们反向剔除一些关键词。例如,几乎每场足球比赛中都有犯规行为。如果我想知道哪些比赛非常激烈,但又没有红牌、黄牌甚至没有犯规,则可以利用布尔模式中的 - 运算符。
-- 查询没有黄牌、红牌和犯规的激烈比赛
SELECT COUNT(*) FROM sport_data_whole
WHERE MATCH (news) AGAINST ('+激烈 -黄牌 -红牌 -犯规' IN BOOLEAN MODE);
返回结果如下:
+----------+
| COUNT(*) |
+----------+
| 0 |
+----------+
1 row in set
步骤三:调优
使用 TOKENIZE 函数进行调优
当发现全文索引的查询结果不符合预期时,通常是因为分词结果不理想。seekdb 提供了一个快速的 TOKENIZE 函数来辅助测试分词效果,该函数支持所有分词器及其对应属性,您可以使用 TOKENIZE 函数验证分词器处理效果。
例如,下面示例的手动分词结果显示,词典中对于国外体育明星人名的支持还不足(如博阿滕、格策),这可能导致用人名进行搜索时效果不佳。
-
使用
TOKENIZE函数验证分词器处理效果:-- 验证中文体育新闻分词效果,使用 ik_smart 分词模式
SELECT TOKENIZE('博阿滕右路反击人球分过传中,格策后点停球转身闪开角度,在门前8米处低射从皮亚托夫裆下钻进门内', 'ik', '[{"additional_args": [{"ik_mode": "smart"}]}]');返回结果如下:
+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| TOKENIZE('博阿滕右路反击人球分过传中,格策后点停球转身闪开角度,在门前8米处低射从皮亚托夫裆下钻进门内', 'ik', '[{"additional_args": [{"ik_mode": "smart"}]}]') |
+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| ["亚", "格", "夫", "阿", "门内", "从", "下钻", "后点", "右路", "分过", "传中", "低", "转身", "球", "射", "闪开", "博", "进", "反击", "门前", "停", "人", "皮", "裆", "策", "滕", "8米处", "托", "在", "角度"] |
+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
1 row in set上述结果并未识别“博阿滕”、“格策”等人名。
-
接下来需要执行下面的语句查询语句是否命中目标文档:
-- 布尔模式搜索特定球员的新闻
SELECT COUNT(*)
FROM sport_data_whole
WHERE MATCH (news) AGAINST ('+格策 +博阿滕' IN BOOLEAN MODE);返回结果如下:
+----------+
| count(*) |
+----------+
| 0 |
+----------+
1 row in set上述结果显式,并未匹配到目标记录。
-
更新系统词典,您可以先将上述中文人名插入到系统词典表。更新系统词典后,需要刷新缓存。
信息如需更新系统词典,建议先联系 OceanBase 技术支持。
-
重建全文索引以应用新词典:
-
删除全文索引 ft_idx1_news。
ALTER TABLE sport_data_whole DROP INDEX ft_idx1_news; -
添加全文索引 ft_idx2_news。
ALTER TABLE sport_data_whole ADD FULLTEXT INDEX ft_idx2_news (news) WITH PARSER IK;
-
-
验证分词器优化效果:
-- 重新执行分词测试(相同输入)
SELECT TOKENIZE('博阿滕右路反击人球分过传中,格策后点停球转身闪开角度,在门前8米处低射从皮亚托夫裆下钻进门内', 'ik', '[{"additional_args": [{"ik_mode": "smart"}]}]');返回结果如下所示:
+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| tokenize('博阿滕右路反击人球分过传中,格策后点停球转身闪开角度,在门前8米处低射从皮亚托夫裆下钻进门内', 'ik', '[{"additional_args": [{"ik_mode": "smart"}]}]') |
+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| ["门内", "从", "下钻", "后点", "右路", "分过", "传中", "低", "转身", "球", "皮亚托夫", "射", "闪开", "进", "反击", "门前", "停", "人", "裆", "8米处", "在", "角度", "格策", "博阿滕"] |
+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
1 row in set上述结果表示,已成功识别专业人名实体。
-
执行下述语句进行搜索准确率验证:
SELECT COUNT(*)
FROM sport_data_whole
WHERE MATCH (news) AGAINST ('+格策 +博阿滕' IN BOOLEAN MODE);返回结果如下所示:
+----------+
| count(*) |
+----------+
| 79 |
+----------+
1 row in set上述结果表示,正确命中 79 条相关记录。
MySQL 性能对比
为了比较 seekdb 与 MySQL 的全文索引性能差异,我们以 MySQL 的全文索引功能作为参考。由于 MySQL 的中文分词能力相对较弱,因此我们选择英文数据集 wikir1k(包含 369721 行,平均每行 100 个词)进行性能对比。
以下是分别在自然语言模式和布尔模式下的多种场景对比结果。可以看到,在需要大量分词或返回结果的场景中,seekdb 的性能表现显著优于 MySQL。对于小结果集,由于计算量占比较小,查询引擎的优势不明显,两个引擎的性能接近。
测试环境:seekdb 的测试规格为 8c 16g,MySQL 版本使用 8.0.36 for Linux on x86_64(MySQL Community Server - GPL)。
自然语言模式
-- q1: 查询包含 "and" 的文档
SELECT * FROM wikir1k WHERE MATCH (document) AGAINST ('and');
-- q2: 查询包含 "and" 的文档,限制返回 10 条
SELECT * FROM wikir1k WHERE MATCH (document) AGAINST ('and') LIMIT 10;
-- q3: 查询包含 "librettists" 的文档
SELECT * FROM wikir1k WHERE MATCH (document) AGAINST ('librettists');
-- q4: 查询包含 "librettists" 的文档,限制返回 10 条
SELECT * FROM wikir1k WHERE MATCH (document) AGAINST ('librettists') LIMIT 10;
-- q5: 查询包含 "alleviating librettists" 的文档
SELECT * FROM wikir1k WHERE MATCH (document) AGAINST ('alleviating librettists');
-- q6: 查询包含 "black spotted white yellow" 的文档
SELECT * FROM wikir1k WHERE MATCH (document) AGAINST ('black spotted white yellow');
-- q7: 查询包含 "black spotted white yellow" 的文档,限制返回 10 条
SELECT * FROM wikir1k WHERE MATCH (document) AGAINST ('black spotted white yellow') LIMIT 10;
-- q8: 查询包含 "between up and down" 的文档
SELECT * FROM wikir1k WHERE MATCH (document) AGAINST ('between up and down');
-- q9: 查询包含 "between up and down" 的文档,限制返回 10 条
SELECT * FROM wikir1k WHERE MATCH (document) AGAINST ('between up and down') LIMIT 10;
-- q10: 查询长文档
SELECT * FROM wikir1k WHERE MATCH (document) AGAINST ('alleviating librettists modifications retelling intangible hydrographic administratively berwickshire strathaven dumfriesshire lesmahagow transhumanist musselburgh prestwick cardiganshire montgomeryshire');
-- q11: 查询长文档,附加 "and"
SELECT * FROM wikir1k WHERE MATCH (document) AGAINST ('alleviating librettists modifications retelling intangible hydrographic administratively berwickshire strathaven dumfriesshire lesmahagow transhumanist musselburgh prestwick cardiganshire montgomeryshire and');
-- q12: 查询长文档,限制返回 10 条
SELECT * FROM wikir1k WHERE MATCH (document) AGAINST ('alleviating librettists modifications retelling intangible hydrographic administratively berwickshire strathaven dumfriesshire lesmahagow transhumanist musselburgh prestwick cardiganshire montgomeryshire and') LIMIT 10;
| 场景 | seekdb | MySQL |
|---|---|---|
| q1 单 token 高频词 | 3820458us | 5718430us |
| q2 单 token 高频词 limit | 231861us | 503772us |
| q3 单 token 低频词 | 879us | 672us |
| q4 单 token 低频词 limit | 720us | 700us |
| q5 多 token 小结果集 | 1591us | 1100us |
| q6 多 token 中结果集 | 259700us | 602221us |
| q7 多 token 中结果集 limit | 25502us | 42620us |
| q8 多 token 大结果集 | 3842391us | 6846847us |
| q9 多 token 大结果集 limit | 301362us | 784024us |
| q10 很多 token 小结果集 | 22143us | 10161us |
| q11 很多 token 大结果集 | 3905829us | 5929343us |
| q12 很多 token 大结果集 limit | 345968us | 769970us |
布尔模式
-- q1: +高频词 -中频词
SELECT * FROM wikir1k WHERE MATCH (document) AGAINST ('+and -which -his' IN BOOLEAN MODE);
-- q2: +高频词 -低频词
SELECT * FROM wikir1k WHERE MATCH (document) AGAINST ('+which (+and -his)' IN BOOLEAN MODE);
-- q3: +中频词 (+高频词 -中频词)
SELECT * FROM wikir1k WHERE MATCH (document) AGAINST ('+and -carabantes -bufera' IN BOOLEAN MODE);
-- q4: +高频词 +低频词
SELECT * FROM wikir1k WHERE MATCH (document) AGAINST ('+and +librettists' IN BOOLEAN MODE);
| 场景 | seekdb | MySQL |
|---|---|---|
| q1: +高频词 -中频词 | 1586657us | 2440798us |
| q2: +高频词 -低频词 | 3726508us | 7974832us |
| q3: +中频词 (+高频词 -中频词) | 3080644us | 5612041us |
| q4: +高频词 +低频词 | 230284us | 357580us |
性能对比总结
从以上的数据对比可以看出,seekdb 在进行复杂的全文搜索时,无论是自然语言模式还是布尔模式,都展现了显著优于 MySQL 的性能。尤其是在处理需要大量分词或返回结果较大的查询中,seekdb 的优势更加明显。这为开发者和数据分析师在选择数据库时提供了有力的参考依据,尤其是在需要高 效搜索海量数据的应用场景中,seekdb 清晰地证明了其强大的性能和灵活的查询能力。
seekdb 全文索引能够在处理复杂查询时始终提供快速的响应时间,更加适合要求高并发、高性能搜索的实际应用场景。
更多操作
更多体验 seekdb 的 AI Native 特性以及尝试基于 seekdb 搭建 AI 应用的使用指导,参见:
- 体验向量搜索
- 体验混合搜索
- 体验 AI 函数服务
- 体验语义索引
- 体验 Cursor Agent + OceanBase MCP 的 Vibe Coding 新范式
- 基于 seekdb 搭建知识库桌面应用
- 基于 seekdb 多模融合构建文旅小助手
- 基于 seekdb 构建图搜图应用
除了使用 SQL 进行操作之外,也支持通过 seekdb 提供的 Python SDK(pyseekdb)进行操作,使用方法参见 通过 Python SDK 体验嵌入式和 pyseekdb 概述。