Elasticsearch Runtime Fields 是什么?和普通 Mapping 字段有什么区别?
简化版
Runtime Fields 是查询时计算出来的字段,不一定提前写入倒排索引或 doc_values。它适合临时修正字段、探索数据和低频计算,但高频查询通常应落成普通字段。
详细版
普通字段在写入时就按 mapping 建索引,查询时速度快但变更成本高。Runtime Fields 把部分计算推迟到查询阶段,灵活性更强。
- 可以在 mapping 中定义,也可以在单次查询里临时定义。
- 适合字段修正、格式转换、临时派生字段、灰度验证 mapping。
- 查询时计算会消耗 CPU,数据量大时可能慢。
- 高频过滤、排序、聚合字段最好提前索引。
- 它是灵活性工具,不是性能优化银弹。
完整版教学
一、为什么需要查询时字段
ES 传统使用方式强调写入时确定 mapping,字段建好索引后查询很快。但真实业务里经常遇到字段建错、日志格式变化、临时要按某个派生字段分析的情况。如果每次都新建索引并 reindex,成本很高。Runtime Fields 提供了一种折中:先在查询时计算一个字段,让你不用立刻改存储结构也能做检索或聚合。
GET logs-*/_search
{
"runtime_mappings": {
"status_family": {
"type": "keyword",
"script": "emit(doc['status'].value.substring(0,1) + 'xx')"
}
},
"query": { "term": { "status_family": "5xx" } }
}
记忆钩子:普通字段是“写入时算好存好”,Runtime Field 是“查询时临时算给你看”。
二、它和普通字段的根本区别
普通字段在写入阶段就建立倒排索引、doc_values 等结构,因此过滤、排序、聚合通常更快。Runtime Field 不一定有这些预计算结构,查询时要读取源字段或 doc_values 并执行脚本计算。这个区别决定了它的定位:提高灵活性,而不是替代所有正式字段。面试时要把“计算时机”讲清楚,否则容易只停留在语法。
| 维度 | 普通字段 | Runtime Field |
|---|---|---|
| 计算时机 | 写入时 | 查询时 |
| 查询性能 | 通常更好 | 取决于脚本和数据量 |
| 变更成本 | 可能要 reindex | 灵活 |
| 适合场景 | 高频查询 | 临时派生、探索 |
三、带数字看查询时计算的成本
假设一个查询命中 100 万条日志,要对每条计算 status_family。即使每条脚本只花 5 微秒,总 CPU 时间也约 5 秒,虽然可以并行分摊到多个 shard,但压力仍然存在。普通字段如果写入时提前存好 status_family,查询只需要走 keyword 结构。这个数字说明 Runtime Field 的灵活性是用查询 CPU 换来的。
1000000 条 × 5 微秒 = 5 秒 CPU
如果每秒有 20 个类似查询,CPU 压力会被快速放大
四、适合哪些临时和过渡场景
Runtime Fields 很适合探索和过渡。比如日志里的 latency_ms 过去是字符串,你想临时转成 long 做范围过滤;或者上线新字段前先用 runtime 计算验证查询逻辑;或者历史索引缺少某个派生字段,短期内又不想重建。它让你能先回答问题,再决定是否把字段物化到写入流程中。
"runtime_mappings": {
"latency_long": {
"type": "long",
"script": "emit(Long.parseLong(doc['latency'].value))"
}
}
五、什么时候应该落成普通字段
一旦某个 runtime field 变成高频过滤、排序、聚合字段,就应该考虑在写入链路里计算并写成普通字段。尤其是排序和聚合会对大量文档访问字段值,如果每次都脚本计算,性能会非常不稳定。工程上常见做法是:先用 runtime field 验证口径,确认稳定后改 ingest pipeline 或应用写入逻辑,后续新索引用普通字段,历史数据按需要 reindex。
验证阶段:runtime field
稳定阶段:ingest pipeline 计算字段
长期阶段:普通 mapping + doc_values
历史修复:按需 reindex
六、脚本和字段缺失要小心
Runtime Field 常用脚本,脚本里要处理字段缺失、类型不一致和异常值。日志数据尤其脏,如果某些文档没有 latency,脚本直接取值可能报错或跳过。生产里要先抽样验证,再用 profile 或慢查询观察成本。不要把复杂业务逻辑写进 runtime script 里长期运行,否则可维护性和性能都会变差。
if (doc.containsKey('latency') && !doc['latency'].empty) {
emit(Long.parseLong(doc['latency'].value));
}
七、常见误区与追问
- 误区:Runtime Field 可以替代 mapping 设计。 它提高灵活性,但高频字段仍应提前建模和索引。
- 误区:查询时计算没有成本。 脚本会消耗 CPU,命中文档越多成本越高。
- 误区:历史字段错了只能立刻 reindex。 可以先用 runtime field 过渡,再规划正式修复。
- 追问:Runtime Field 适合排序吗? 低频可以,高频大数据排序不建议,最好落成普通字段。
- 追问:如何把 runtime 字段正式化? 在写入链路或 ingest pipeline 中计算字段,更新 mapping,新数据直接写入,历史按需 reindex。
八、加强记忆
Runtime Fields 记成“查询时临时生成的字段”。它的关键词是灵活、过渡、探索;它的代价是 CPU、脚本复杂度和高频查询不稳定。面试回答先对比普通字段的计算时机,再给适用场景,最后强调稳定字段要物化,这样就不会把它误讲成性能神器。