跳到主要内容

体验全文索引

全文索引介绍

全文索引(Full-Text Index)是数据库中用于加速文本数据搜索的特殊索引类型,特别适用于处理包含大量文本字段(如文章内容、评论、博客等)的查询需求。它支持快速的关键词匹配查询,可以在文本中查找一个或多个词语,并返回相关结果。全文索引常常应用于搜索引擎和文本分析系统中,有效帮助企业快速定位关键信息,提升搜索效率。

在企业的实际生产中,全文索引功能可以应用于系统日志分析、用户分析等众多场景,全文索引能够对数据做到高效率过滤筛选、或是高质量相关性评估。在 AI 领域,seekdb 基于稀疏稠密向量与全文索引相结合的多路召回架构,能在具有特殊知识领域的 RAG 系统中实现更高效、更精准的召回效果。

全文索引的主要功能包括:

  • 全文搜索:通过构建全文索引,能够对整个文档或大段文本内容进行全面的索引,从而实现更灵活、高效的搜索。

  • 快速查找:用户可以根据输入的关键词在数据库中快速查找匹配的文本,大大减少了搜索时间。

  • 高效处理大量文本:全文索引能够有效处理各种类型的文本数据,包括文章、报告、网页和电子邮件等,为用户提供精准且快速的搜索体验。

  • 支持复杂查询:除了基础的关键词搜索,全文索引还能支持复杂的查询需求,例如近似匹配和相关性排序,极大地丰富了数据库的搜索能力。

通过引入全文索引功能,seekdb 可以在面对大规模文本数据和复杂搜索需求时,显著提高查询性能,使得用户能够更高效地获取所需信息。

使用全文索引实现海量新闻数据的高效搜索

以下示例以新闻资讯类业务场景为例,演示通过使用全文索引功能如何在海量信息中快速找到目标新闻。我们将通过查询中的关键词,展示 seekdb 全文索引在功能、性能和易用性方面的提升。

前提条件

要顺利操作并体验 seekdb 的全文索引功能,请确保满足以下前提条件:

  1. 环境要求:您已部署 seekdb。

  2. 数据库创建:确保您已创建数据库。如需了解详细步骤,请参见 创建数据库

操作步骤

以下操作步骤将引导您体验 seekdb 的全文索引及常用视图和查询技巧。

步骤一:导入数据集

seekdb 内置了支持中文的 IK 分词器,以及相较于传统自然语言处理更为高效的布尔模式。我们将使用 中文足球体育新闻数据集 导入数据到 seekdb 中,创建一张名为 sport_data_whole 的无主键分区表,包含三列变长字符(eventdatenews),并使用 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 函数验证分词器处理效果。

例如,下面示例的手动分词结果显示,词典中对于国外体育明星人名的支持还不足(如博阿滕、格策),这可能导致用人名进行搜索时效果不佳。

  1. 使用 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

    上述结果并未识别“博阿滕”、“格策”等人名。

  2. 接下来需要执行下面的语句查询语句是否命中目标文档:

    -- 布尔模式搜索特定球员的新闻
    SELECT COUNT(*)
    FROM sport_data_whole
    WHERE MATCH (news) AGAINST ('+格策 +博阿滕' IN BOOLEAN MODE);

    返回结果如下:

    +----------+
    | count(*) |
    +----------+
    | 0 |
    +----------+
    1 row in set

    上述结果显式,并未匹配到目标记录。

  3. 更新系统词典,您可以先将上述中文人名插入到系统词典表。更新系统词典后,需要刷新缓存。

    信息

    如需更新系统词典,建议先联系 OceanBase 技术支持。

  4. 重建全文索引以应用新词典:

    1. 删除全文索引 ft_idx1_news。

      ALTER TABLE sport_data_whole DROP INDEX ft_idx1_news;
    2. 添加全文索引 ft_idx2_news。

      ALTER TABLE sport_data_whole ADD FULLTEXT INDEX ft_idx2_news (news) WITH PARSER IK;
  5. 验证分词器优化效果:

    -- 重新执行分词测试(相同输入)
    SELECT TOKENIZE('博阿滕右路反击人球分过传中,格策后点停球转身闪开角度,在门前8米处低射从皮亚托夫裆下钻进门内', 'ik', '[{"additional_args": [{"ik_mode": "smart"}]}]');

    返回结果如下所示:

    +---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
    | tokenize('博阿滕右路反击人球分过传中,格策后点停球转身闪开角度,在门前8米处低射从皮亚托夫裆下钻进门内', 'ik', '[{"additional_args": [{"ik_mode": "smart"}]}]') |
    +---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
    | ["门内", "从", "下钻", "后点", "右路", "分过", "传中", "低", "转身", "球", "皮亚托夫", "射", "闪开", "进", "反击", "门前", "停", "人", "裆", "8米处", "在", "角度", "格策", "博阿滕"] |
    +---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
    1 row in set

    上述结果表示,已成功识别专业人名实体。

  6. 执行下述语句进行搜索准确率验证:

    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;
场景seekdbMySQL
q1 单 token 高频词3820458us5718430us
q2 单 token 高频词 limit231861us503772us
q3 单 token 低频词879us672us
q4 单 token 低频词 limit720us700us
q5 多 token 小结果集1591us1100us
q6 多 token 中结果集259700us602221us
q7 多 token 中结果集 limit25502us42620us
q8 多 token 大结果集3842391us6846847us
q9 多 token 大结果集 limit301362us784024us
q10 很多 token 小结果集22143us10161us
q11 很多 token 大结果集3905829us5929343us
q12 很多 token 大结果集 limit345968us769970us

布尔模式

-- 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);
场景seekdbMySQL
q1: +高频词 -中频词1586657us2440798us
q2: +高频词 -低频词3726508us7974832us
q3: +中频词 (+高频词 -中频词)3080644us5612041us
q4: +高频词 +低频词230284us357580us

性能对比总结

从以上的数据对比可以看出,seekdb 在进行复杂的全文搜索时,无论是自然语言模式还是布尔模式,都展现了显著优于 MySQL 的性能。尤其是在处理需要大量分词或返回结果较大的查询中,seekdb 的优势更加明显。这为开发者和数据分析师在选择数据库时提供了有力的参考依据,尤其是在需要高效搜索海量数据的应用场景中,seekdb 清晰地证明了其强大的性能和灵活的查询能力。

seekdb 全文索引能够在处理复杂查询时始终提供快速的响应时间,更加适合要求高并发、高性能搜索的实际应用场景。

更多操作

更多体验 seekdb 的 AI Native 特性以及尝试基于 seekdb 搭建 AI 应用的使用指导,参见:

除了使用 SQL 进行操作之外,也支持通过 seekdb 提供的 Python SDK(pyseekdb)进行操作,使用方法参见 通过 Python SDK 体验嵌入式pyseekdb 概述