大数据 -184 Elasticsearch Doc Values 机制详解:列式存储如何支撑排序 / 聚合 / 脚本

TL;DR
场景:ES 排序/聚合/脚本访问字段值时,倒排索引不擅长按“列”读数据
结论:Doc Values 是磁盘列式结构(除 text 外多数类型默认开启),用来高效读字段值
产出:给出可落地的字段选型、mapping 约束、常见报错定位与修复速查
版本矩阵
DocValues 机制
Doc Values 是一种以列式存储的索引机制,用于在检索时优化磁盘读取操作。与传统的行式存储不同,Doc Values 将每个字段的所有值按列组织存储,这种结构特别适合分析型查询场景。以下是其核心特点:
存储结构对比:
反向索引:基于倒排表的结构,主要用于全文检索场景,可以快速查找文档中包含某个词的情况
Doc Values:采用紧凑的列式存储格式,每个字段的值被连续存储在磁盘上,支持高效的顺序读取
性能优势:
内存效率:仅需加载查询涉及的列,而非整个文档
CPU 缓存友好:连续存储的列数据能更好利用 CPU 缓存行
压缩率高:同列数据通常具有相似性,可采用更高效的压缩算法
典型应用场景:
排序场景:
示例:对商品按价格排序时,只需顺序读取 price 列的 Doc Values
优势:相比加载完整文档,可减少 90%以上的 IO 操作
聚合场景:
支持操作:terms、sum、average、percentiles 等
实际案例:计算某时间段内订单金额的分布情况
性能对比:比使用脚本计算快 5-10 倍
过滤场景:
适用操作:范围过滤(price>100)、精确匹配(status="active")等
实现原理:通过 bitmap 快速筛选符合条件的文档 ID
特殊优化:对数值类型支持位图索引加速
实现细节:
默认开启:在 Elasticsearch 等系统中通常默认启用
存储位置:既支持内存映射也支持磁盘持久化
更新机制:采用追加写方式,定期合并优化
使用建议:
适合字段:高基数字段、需要频繁聚合/排序的字段
不适合场景:大文本字段(超过几 KB)、很少被查询的字段
配置参数:可通过"doc_values: false"显式关闭
通过合理使用 Doc Values,可以显著提升分析型查询的性能,特别是在处理大数据集时效果更为明显。
为什么要有 Doc Values
Elasticsearch 之所以搜索这么迅速,归功于它的倒排索引设计,然后它也不是万能的,倒排索引的检索性能是非常快的,但是在字段排序时却不是理想的结构:
如上面的内容中可以看出,它只有词对应 doc,但是并不知道每一个 doc 中的内容,那么如果想排序的话每一个 doc 都去获取一次文档内容岂不是非常耗时?DocValues 的出现使得这个问题迎刃而解。字段的 doc_values 属性有两个值,true、false,默认是 true,即开启。当 doc_values 为 false 时,无法基于该字段排序、聚合、在脚本中访问字段值。当 doc_values 为 true 时,ES 会增加一个相应的正排索引,这增加的磁盘占用,也会导致索引数据速度慢一些。
Doc Values 是列式存储的,这意味着每个字段值都以列的形式存储在磁盘中,而不是像原始文档那样存储在行中。这种方式有助于优化数据的读取,因为在执行排序或聚合时,Elasticsearch 只需访问与操作相关的字段,而不需要加载整个文档。
每个文档的字段值在索引时被预处理,并以压缩的形式存储为 Doc Values,这些值会以内存映射文件(memory-mapped file)的方式加载到内存中,以便进行快速读取。
Doc Values 举例
创建一个索引:
写入相对应的数据:
执行结果如下图所示:
进行全量查询,确认一下数据的情况:
执行结果如下图所示:
什么是 Doc Values
Doc Values 通过转置倒排索引和正排索引两者间的关系来解决这个问题,倒排索引将词项映射到包含它的文档:
当数据被转置后,想要收集到每个文档行,获取所有的词项就非常简单了。
深入理解 ES Doc Values
DocValues 是 Lucene 索引机制中的重要组成部分,它与倒排索引同时生成,具有以下特性:
生成机制:
在索引创建阶段,DocValues 会与倒排索引并行构建
基于 Segment 级别生成,每个 Segment 都包含自己独立的 DocValues 数据
生成后是不可变的,与倒排索引保持一致性
存储特性:
采用高效的序列化方式将数据结构持久化到磁盘
存储格式经过优化,支持快速随机访问和顺序扫描
与倒排索引共享相同的 Segment 合并策略
内存管理优势:
利用操作系统文件缓存机制,而非 JVM 堆内存
当数据量小于系统可用内存时(workingset < RAM):
操作系统会自动将频繁访问的 DocValues 保留在内存中
实现接近内存数据库的访问速度
典型场景:过滤、排序、聚合等操作能获得极快响应
大容量处理能力:
当数据量超过内存容量时(workingset >> RAM):
操作系统会自动将不活跃数据换出到磁盘
虽然访问速度有所下降,但避免了 OOM 风险
典型场景:处理数十亿文档时仍能保持稳定性能
应用场景示例:
电商平台中百万级商品的价格排序
日志分析系统中的时间范围过滤
大数据分析中的分组聚合计算
这种设计使 DocValues 在保持高性能的同时,也能处理远超物理内存容量的数据集,为大规模数据应用提供了可靠的底层支持。
DocValues 压缩
从广义来说,DocValues 本质上是一个序列化的列式存储,这个结构非常适用于聚合、排序、脚本等操作。而且,这种存储方式非常的便于压缩,特别是数字类型,这样可以减少磁盘空间并且提高访问速度。下面我们看一组数字类型的 DocValues:
你会注意到这里每个数字都是 100 的倍数,DocValues 会检测一个段里面的所有数值,并使用一个最大公约数,方便做进一步的数据压缩,我们可以对每个数字都除以 100,然后得到:[1,10,15,12,3,19,42]。现在这些数字变小了,只需要很少的位就可以存储下,也减少了磁盘存放的大小。
DocValues 在压缩过程中使用如下技巧,它会依次检测以下压缩模式:
如果所有的数值各不相同(或缺失),设置一个标记并记录这些值
如果这些值小于 256,将使用一个简单的编码表
如果这些值大于 256,检测是否存在一个最大公约数
如果没有存在最大公约数,从最小的数值开始,统一计算偏移量进行编码当然如果存储 String 类型,其一样可以通过顺序表对 String 类型进行数字编码,然后再把数字类型构建 DocValues。
禁用 Doc Values
DocValues 默认对所有字段启动,除了 analyzed strings。也就是说所有的数字、地理坐标、日期、IP 和不分析(not_analyzed)字符类型都会默认开启。analyzed strings 暂时还不能使用 DocValues,是因为经过分析以后得文本会生成大量的 Token,这样非常影响性能。虽然 DocValues 非常好用,但是如果你存储的数据确实不需要这个特性,就不如禁用它,这样不仅节省磁盘空间,也许会提升索引的速度。要禁用 DocValues,在字段的映射 mapping 设置 doc_values:false 即可。例如,这里我们创建了一个新的索引,字段 session_id 禁用了 DocValues:
通过设置 doc_values:false,这个字段将不能被用于聚合、排序以及脚本操作
带来的优势
减少内存使用:由于 Doc Values 将字段值存储在磁盘上并在需要时读取,因此相比内存中保持字段值的方式(例如 fielddata),它极大地减少了内存的使用。
高效的磁盘读取:Doc Values 的列式存储意味着在执行排序或聚合操作时,Elasticsearch 可以只加载所需的字段值,而不必加载整个文档。
提高排序和聚合的性能:对于经常需要排序或聚合的字段,Doc Values 可以显著提高性能,因为它优化了读取路径。
使用场景
排序:例如,用户需要根据时间戳排序查询结果,Doc Values 会提供优化的列式存储,直接从磁盘读取时间戳的值进行排序,而不需要加载整个文档。
聚合:在执行例如统计某个字段的平均值、最大值或分布情况时,Doc Values 可以极大地提高查询的响应速度,因为只需读取相关字段即可。
范围查询:例如查找价格在一定范围内的文档时,Doc Values 允许快速扫描价格字段而不涉及文档的其他内容。
错误速查
其他系列
🚀 AI 篇持续更新中(长期更新)
AI 炼丹日志-29 - 字节跳动 DeerFlow 深度研究框斜体样式架 私有部署 测试上手 架构研究,持续打造实用 AI 工具指南!AI 研究-132 Java 生态前沿 2025:Spring、Quarkus、GraalVM、CRaC 与云原生落地🔗 AI模块直达链接
💻 Java 篇持续更新中(长期更新)
Java-196 消息队列选型:RabbitMQ vs RocketMQ vs KafkaMyBatis 已完结,Spring 已完结,Nginx 已完结,Tomcat 已完结,分布式服务已完结,Dubbo 已完结,MySQL 已完结,MongoDB 已完结,Neo4j 已完结,FastDFS 已完结,OSS 已完结,GuavaCache 已完结,EVCache 已完结,RabbitMQ 正在更新... 深入浅出助你打牢基础!🔗 Java模块直达链接
📊 大数据板块已完成多项干货更新(300 篇):
包括 Hadoop、Hive、Kafka、Flink、ClickHouse、Elasticsearch 等二十余项核心组件,覆盖离线+实时数仓全栈!大数据-278 Spark MLib - 基础介绍 机器学习算法 梯度提升树 GBDT 案例 详解🔗 大数据模块直达链接
版权声明: 本文为 InfoQ 作者【武子康】的原创文章。
原文链接:【http://xie.infoq.cn/article/10c997a4c62e9c9e8974c610c】。文章转载请联系作者。







评论