Apache Doris倒排索引工作原理与全文检索加速
markdown-body{word-break:break-word;line-height:1 75;font-weight:400;font-size:16px;overflow-x:hidden;color: 252933} markdown-body h1, markdown-body
在传统 OLAP 场景中,数据库非常擅长处理规则明确、可预期的大规模聚合分析查询。例如“统计本季度各品类的平均评分”,这类 SQL 可以借助物理分区、排序聚簇和列式存储,让计算引擎以数据块为单位高效读取并完成计算。
但在面向终端用户的实时分析场景里,查询通常是临时发起且难以预判的。以服务数百万卖家的电商平台为例:每个卖家登录后台后,只能查看属于自己的业务数据。他们最常发起的分析查询通常包括:
- 查看我账号下全部商品的用户评价
- 统计我上架的某个产品的总销量与销售收入
- 在电子产品评论列表中,筛选提到“电池续航”的用户评论
对底层分析型数据库而言,平台往往需要同时承载数百万卖家的这类请求。每一条查询,都像是在一片包含数亿行记录的数据海洋中,去精准找出分散在不同位置的几百条目标数据。如果缺少针对性的索引能力,数据库引擎就无法有效跳过无关数据,只能以较高成本执行全表扫描。这正是传统 OLAP 数据库在处理“稀疏扫描”或低命中率查询场景时长期面临的核心瓶颈。
为了解决这个问题,不少团队会在 OLAP 之外额外引入 Elasticsearch 架构,但随之而来的则是双引擎维护、数据同步链路、Schema 变更适配等复杂工程负担。
那么,是否可以直接在 OLAP 引擎内部解决稀疏扫描和全文检索问题?
我们基于开源分析型数据库 Apache Doris,针对包含 1.35 亿条数据的亚马逊评论数据集进行了 50 并发压力测试。测试结果显示:引入内置倒排索引后,全文搜索性能提升 59 倍,商品查找提速 14 倍,多维组合查询提速 10 倍。
传统 OLAP 数据库处理稀疏扫描的局限
传统 OLAP 数据库(如 AWS Redshift)之所以在海量数据分析场景中表现优异,主要依赖以下三项关键技术来尽可能减少扫描数据量:
- 列式存储:只读取查询涉及的列,显著降低磁盘 I/O 成本;
- 物理排序:按照指定字段(排序键)对数据进行物理聚簇存储;
- 数据块映射(Zone Maps):记录每个存储块(Data Block)中的最小值和最大值,从而实现数据块级过滤(Data Skipping)。
这套机制在处理密集扫描(Dense Scan)类查询时通常非常高效。
以一张按评论日期(review_date)排序、包含 1.35 亿条亚马逊评论数据的表为例:
CREATE TABLE amazon_reviews (
review_date INT NULL,
marketplace VARCHAR(20) NULL,
customer_id BIGINT NULL,
review_id VARCHAR(40) NULL,
product_id VARCHAR(10) NULL,
product_parent BIGINT NULL,
product_title VARCHAR(500) NULL,
product_category VARCHAR(50) NULL,
star_rating SMALLINT NULL,
helpful_votes INT NULL,
total_votes INT NULL,
vine BOOLEAN NULL,
verified_purchase BOOLEAN NULL,
review_headline VARCHAR(500) NULL,
review_body STRING NULL
)
DUPLICATE KEY(review_date)
DISTRIBUTED BY HASH(review_date) BUCKETS 16
PROPERTIES ("compression" = "ZSTD");
1、密集扫描:查询本季度平均评分
由于数据已经按照 review_date 排序,本季度相关记录在物理存储上会高度集中。数据库引擎只需读取 Zone Maps 中记录的日期范围,就能跳过绝大多数无关时间段的数据块,仅扫描少量相关聚簇数据即可完成查询。
2、稀疏扫描:查询产品 B00BGGDVOO 的全部评论
该产品在数年间陆续产生的 14,200 条评论,在物理存储上呈现离散分布,这时原有机制就难以发挥作用。
- 排序键无法辅助过滤:数据按日期排序,无法为产品 ID 查询提供直接索引;
- Zone Maps 难以裁剪数据块:数据集中约有 2,000 万个独立产品 ID,几乎每个数据块中都可能混杂该产品的评论记录,导致 Zone Maps 的极值范围失去有效过滤能力。
最终,数据库引擎只能被动扫描全部 1.35 亿行数据,仅为了找出其中占比约 0.01% 的目标记录。在 50 个并发线程的压力下,这类查询的延迟会明显增加,系统吞吐能力(QPS)也会受到显著影响。
物理存储顺序本质上是单维度的。如果把排序键改成 product_id,虽然商品查询会更快,但按日期进行的范围分析又可能退化为全表扫描。也就是说,单一的物理数据组织方式很难同时兼顾两种完全不同的查询访问模式。
因此,很多团队最终会采用 Elasticsearch + OLAP 的双引擎方案:由 Elasticsearch 负责稀疏点查、关键词检索和全文搜索,分析型数据库继续承担大规模聚合分析任务。
但这种架构也会带来明显的工程复杂度:团队不仅要建设并维护实时数据同步链路,还要在两套系统之间同步 Schema 变更,并在业务层编写查询路由逻辑。在生产环境中,这类基础设施往往意味着更高的运维成本、更复杂的一致性保障,以及更重的系统治理压力。
倒排索引应对稀疏扫描的技术机制
稀疏扫描的核心难题在于:如何在不做全表扫描的前提下,快速且准确地定位物理存储中离散分布的目标行?
倒排索引通过建立“数据值到行位置(Row ID)”的直接映射,打破了传统 OLAP 依赖顺序扫描的执行方式。在实际查询过程中,数据引擎不必再逐块解压、逐列读取全部数据,而是先访问索引获取满足条件的 Row ID 列表,再精确跳转到对应位置读取目标数据。
针对不同字段类型和查询模式,倒排索引在底层采用了不同的数据结构与实现方式:
字符串精确匹配:Posting List(倒排列表)
传统正向存储记录的是“行到值”的映射关系(按行找属性);而倒排索引记录的是“值到行”的映射关系(按值找行)。例如:
正向索引:
- 第 1 行 →
customer_id: A1B2C3D4 - 第 2 行 →
customer_id: E5F6G7H8 - 第 3 行 →
customer_id: A1B2C3D4
倒排索引:
A1B2C3D4→[Row 1, 3]E5F6G7H8→[Row 2]
当执行 WHERE customer_id = 'A1B2C3D4' 时,引擎通过检索倒排列表即可直接得到目标集合 [1, 3],从而完全跳过第 2 行以及其他所有不匹配的数据扫描。
数值与范围过滤:BKD 树
对于 star_rating(评分)、helpful_votes(有用投票数)等数值型字段,如果简单地为每个数值都建立独立 Posting List,那么在范围查询场景中的效率并不理想。为此,系统采用了更适合多维数值过滤的 BKD 树 数据结构。
BKD 树通过递归划分子空间,将连续数值组织为有序叶子节点与数据块:
- 点查询(如
WHERE star_rating = 5):借助 BKD 树节点的二分查找能力,可在毫秒级定位目标叶子节点并提取对应 Row ID。 - 范围查询(如
WHERE helpful_votes BETWEEN 100 AND 500):根据树结构中的区间重叠关系,只遍历与[100, 500]存在交集的叶子节点,快速汇总符合条件的 Row ID 集合。
非结构化文本检索:分词器 + Posting List
全文检索首先会把文本内容切分成可检索词条,再为每个词条建立独立的倒排列表:
-
原文:
“电池续航差,耗电快” -
分词与倒排映射:
"电池"→[Row 1, 3, 7, 15, 23]"续航"→[Row 1, 8, 15, 23]"差"→[Row 1, 2, 5, 7, 15, 19]
当执行全文搜索查询(例如检索同时包含 "电池" 和 "差" 的评论)时,引擎无需逐行做字符串匹配,只需读取这两个词条对应的 Posting List,再通过高效位图运算求交集,就能准确锁定目标文本行。
不同的查询类型,对倒排列表(Posting List)的组合策略也并不相同:
基准测试:Apache Doris 倒排索引性能验证
为了验证倒排索引对稀疏查询和全文检索性能的提升效果,我们在包含 1.35 亿条数据的亚马逊评论数据集上进行了基准测试。在基础表结构之上,额外创建了 7 个倒排索引,覆盖高频查询字段:客户 ID、产品 ID、评论 ID、评分、有用投票数,以及两个文本字段。
CREATE TABLE IF NOT EXISTS amazon_reviews (
review_date INT NULL,
marketplace VARCHAR(20) NULL,
customer_id BIGINT NULL,
review_id VARCHAR(40) NULL,
product_id VARCHAR(10) NULL,
product_parent BIGINT NULL,
product_title VARCHAR(500) NULL,
product_category VARCHAR(50) NULL,
star_rating SMALLINT NULL,
helpful_votes INT NULL,
total_votes INT NULL,
vine BOOLEAN NULL,
verified_purchase BOOLEAN NULL,
review_headline VARCHAR(500) NULL,
review_body STRING NULL,
INDEX idx_customer_id (customer_id) USING INVERTED,
INDEX idx_product_id (product_id) USING INVERTED,
INDEX idx_review_id (review_id) USING INVERTED,
INDEX idx_star_rating (star_rating) USING INVERTED,
INDEX idx_helpful_votes (helpful_votes) USING INVERTED,
INDEX idx_review_body (review_body) USING INVERTED PROPERTIES("parser" = "english"),
INDEX idx_review_headline (review_headline) USING INVERTED PROPERTIES("parser" = "english")
)
DUPLICATE KEY(review_date)
DISTRIBUTED BY HASH(review_date) BUCKETS 16
PROPERTIES ("compression" = "ZSTD");
测试环境配置
- 硬件节点:16 核 CPU,128GB 内存(单计算节点)
- 数据集:1.35 亿行,26GB(基础大小),47GB(含索引)
- 并发线程:50 个并发线程
- 测试工具:JMeter(关闭 SQL 缓存,以获得更真实的引擎执行耗时)
测试查询集
本次测试选取了 7 个具有代表性的稀疏查询场景,覆盖精确匹配、范围过滤、多条件组合检索、全文搜索以及文本情感分析等常见业务需求:
-- Q3:客户历史记录(基于 customer_id 倒排列表)
SELECT product_category, COUNT(*) as reviews, A VG(star_rating) as a vg_rating,
SUM(helpful_votes) AS total_helpful
FROM amazon_reviews
WHERE customer_id = 53096570
GROUP BY product_category ORDER BY reviews DESC;
-- Q4:产品评论分析(基于 product_id 倒排列表)
SELECT star_rating, COUNT(*) as count, A VG(helpful_votes) as a vg_helpful
FROM amazon_reviews
WHERE product_id = 'B00BGGDVOO'
GROUP BY star_rating ORDER BY star_rating DESC;
-- Q5:按 ID 精确查找评论(基于 review_id 倒排列表)
SELECT * FROM amazon_reviews WHERE review_id = 'R1NQ5RXN1LZ0YW';
-- Q6:基于评分与投票数的范围筛选(基于 BKD 树)
SELECT product_category, COUNT(*) as reviews, A VG(helpful_votes) as a vg_helpful
FROM amazon_reviews
WHERE star_rating >= 4 AND helpful_votes >= 50 AND review_date >= 16071
GROUP BY product_category ORDER BY a vg_helpful DESC LIMIT 10;
-- Q7:多维组合筛选(倒排列表 + BKD 树)
SELECT product_category, product_title, star_rating, helpful_votes, review_headline
FROM amazon_reviews
WHERE customer_id = 16378095
AND star_rating <= 2
AND helpful_votes >= 10
ORDER BY helpful_votes DESC LIMIT 20;
-- Q9:全文搜索(分词倒排索引 MATCH_ALL)
SELECT product_id, product_title, star_rating, review_headline,
LEFT(review_body, 200) AS review_snippet
FROM amazon_reviews
WHERE review_body MATCH_ALL 'battery life poor'
AND product_category = 'Electronics'
ORDER BY star_rating ASC LIMIT 20;
-- Q16:文本情感筛选(分词处理 + BKD 树 + 聚合)
SELECT product_id, product_title, COUNT(*) as negative_reviews,
A VG(star_rating) as a vg_rating
FROM amazon_reviews
WHERE product_category = 'Electronics'
AND review_body MATCH_ALL 'defective broken'
AND star_rating <= 2
GROUP BY product_id, product_title ORDER BY negative_reviews DESC LIMIT 20;
测试结果分析
测试数据表明,倒排索引在绝大多数稀疏查询场景下都展现出显著的性能提升:
- Q3(客户历史记录查询):由于该字段为
BIGINT类型,原生列式扫描本身已经具备较高效率,因此索引带来的增益相对有限。但随着数据规模持续扩大,倒排索引仍然具有明显的潜在优化价值。 - Q5(按 ID 精确查找):性能提升达到 156 倍。对于 1.35 亿行数据中的单条记录查找,全表扫描成本非常高,而倒排列表可以直接命中目标行,显著减少 I/O 与计算开销。
- Q9(全文搜索):性能提升达到 59 倍。如果没有分词倒排索引,全文检索通常需要逐行执行字符串匹配;引入索引后,可大幅缩短查询耗时,实现接近毫秒级的搜索响应。
资源开销与适用场景评估
当然,建立倒排索引并非没有成本,它会带来一定的存储占用和写入性能开销。在新增 7 个倒排索引之后:
- 存储占用:数据文件总大小从 26 GB 增加到 47 GB(增幅约 82%),额外空间主要来自两个大文本字段的倒排索引;
- 写入速度:数据导入耗时从 488 秒增加到 519 秒(写入速度下降约 6%)。
如果只对 ID 字段和数值字段建立索引,存储与写入带来的额外成本通常会明显降低。因此,建议结合实际业务查询模式,优先为高频过滤列、精确匹配列和全文搜索字段建立索引。
结束语
传统 OLAP 数据库针对大批量数据的密集扫描和聚合分析做了大量优化,因此在统计分析、报表计算等场景中表现出色。但在涉及商品精确查找、客户历史查询、评论全文搜索等稀疏定位需求时,由于扫描范围大、命中率低,查询性能往往会明显下降。
倒排索引的关键价值,就在于把“值到行位置”的映射变成了可直接访问的索引能力:精确匹配依赖倒排列表,范围过滤交给 BKD 树,文本检索则通过分词索引完成。这样一来,数据库引擎不再需要“大海捞针”,而是能够快速锁定目标行。基于 1.35 亿条亚马逊评论数据集的并发测试结果显示,引入倒排索引后,稀疏查询与全文检索场景的整体性能可提升 10 倍到 59 倍。
当然,这一技术方案需要在存储成本与写入性能之间做适度权衡,而这部分开销通常主要取决于文本列索引的规模。对于当前仍在维护“分析型数据库 + Elasticsearch”双引擎架构的团队来说,评估并引入 OLAP 内置倒排索引能力,是简化技术栈、降低运维复杂度、提升稀疏查询性能的一条有效路径。
欢迎阅读 Apache Doris 官方文档下载使用;如果希望以更低运维成本快速开箱即用,也可以考虑使用 SelectDB 云服务。
游乐网为非赢利性网站,所展示的游戏/软件/文章内容均来自于互联网或第三方用户上传分享,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系youleyoucom@outlook.com。
同类文章
Redis是什么:核心特性、架构与应用场景解析
Redis是一款基于内存的键值型NoSQL数据库,以超高读写速度和丰富的数据结构著称。本文系统梳理Redis的核心特性、架构组成、性能优势及典型应用场景,并通过与Memcached、MySQL、MongoDB的对比,帮助开发者快速判断Redis是否适合当前业务需求。
Windows 安装 MongoDB 完整图文教程
本文详细介绍在 Windows 系统上安装 MongoDB 的完整流程。从官网下载 MSI 安装包开始,逐步演示自定义安装路径、配置 Windows 服务、跳过 MongoDB Compass 等关键选项,并提供通过系统服务列表验证安装是否成功的方法,帮助开发者快速搭建本地 MongoDB 环境。
Linux 安装 MongoDB 完整指南:依赖配置、环境变量与服务启动
本文详解在 Linux 系统下安装 MongoDB 的完整流程,涵盖依赖包安装、二进制包下载解压、环境变量配置、数据与日志目录创建及服务启动验证。通过标准化命令与路径说明,帮助开发者快速完成部署并确认服务状态。
MacOS安装MongoDB完整教程
本文介绍在MacOS系统下安装MongoDB的完整流程,涵盖下载、解压、目录配置、环境变量设置及服务启动。通过明确的命令与参数说明,帮助开发者快速完成环境搭建并验证安装结果。
Ubuntu系统安装与配置Redis完整指南
本文详解在Ubuntu系统中安装Redis的两种主流方式:apt在线安装与源码编译安装。涵盖版本选择逻辑、服务启停与状态检查、连接验证方法,以及在线练习工具与桌面GUI客户端的对比与使用建议,帮助开发者快速搭建并验证Redis运行环境。
- 热门数据榜
1
2
3
4
5
6
7
8
9
10
1
2
3
4
5
6
7
8
9
10
相关攻略
2026-09-01 06:20
2026-09-01 06:20
2026-09-01 06:20
2026-09-01 06:19
2026-09-01 06:19
2026-09-01 06:19
2026-09-01 06:18
2026-09-01 06:18
热门教程
- 游戏攻略
- 安卓教程
- 苹果教程
- 电脑教程

