曾经为索引中漏掉的字段而头疼?是否曾为多个服务索引结构不一而不得不写复杂的应用层代码?是否梦想过把一段复杂的查询逻辑像"视图"一样到处复用?Elasticsearch 的 ES|QL 三大能力------
逻辑视图(Logical Views)
FROM 子查询(Subqueries)
*读时模式(Schema-on-Read)
一、一次查询,统揽全局
在 9.x 版本中,ES|QL(Elasticsearch Query Language)迎来了自诞生以来最大规模的数据访问能力升级。这次升级直击三个核心痛点:
- 映射遗漏:数据已经索引,但忘记在 mapping 中定义某些字段,难道要重新索引?不必了。
- 索引异构:不同微服务的日志索引结构各异,如何在一个查询中无缝合并分析?
- 逻辑复用:一段精心调优的过滤和转换逻辑,能否像数据库视图一样被所有仪表盘和告警引用?
新版本给出的答案是三个环环相扣的特性:
| 特性 | 解决痛点 | 状态 |
|---|---|---|
| 逻辑视图(Logical Views) | 查询逻辑复用、统一管理 | Tech Preview |
| FROM 子查询(Subqueries) | 合并异构索引,各自独立管道 | Tech Preview |
| 读时模式(Schema-on-Read) | 查询未映射字段,无需重新索引 | Tech Preview |
与此同时,时区支持(Timezone) 正式 GA,LIMIT BY 原生支持分组 Top-N,LOOKUP JOIN 性能大幅优化,还有一批新函数和管道命令加入。本文会带你逐一领略,并用 Mermaid 流程图 直观展示内部运作机制。
二、三大能力协同工作
先看一张全景图,理解 Views、Subqueries 和 Schema-on-Read 是如何叠加起来产生化学反应的:
#mermaid-svg-hvQzNxwos4g5Jm2m{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-hvQzNxwos4g5Jm2m .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-hvQzNxwos4g5Jm2m .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-hvQzNxwos4g5Jm2m .error-icon{fill:#552222;}#mermaid-svg-hvQzNxwos4g5Jm2m .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-hvQzNxwos4g5Jm2m .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-hvQzNxwos4g5Jm2m .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-hvQzNxwos4g5Jm2m .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-hvQzNxwos4g5Jm2m .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-hvQzNxwos4g5Jm2m .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-hvQzNxwos4g5Jm2m .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-hvQzNxwos4g5Jm2m .marker{fill:#333333;stroke:#333333;}#mermaid-svg-hvQzNxwos4g5Jm2m .marker.cross{stroke:#333333;}#mermaid-svg-hvQzNxwos4g5Jm2m svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-hvQzNxwos4g5Jm2m p{margin:0;}#mermaid-svg-hvQzNxwos4g5Jm2m .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-hvQzNxwos4g5Jm2m .cluster-label text{fill:#333;}#mermaid-svg-hvQzNxwos4g5Jm2m .cluster-label span{color:#333;}#mermaid-svg-hvQzNxwos4g5Jm2m .cluster-label span p{background-color:transparent;}#mermaid-svg-hvQzNxwos4g5Jm2m .label text,#mermaid-svg-hvQzNxwos4g5Jm2m span{fill:#333;color:#333;}#mermaid-svg-hvQzNxwos4g5Jm2m .node rect,#mermaid-svg-hvQzNxwos4g5Jm2m .node circle,#mermaid-svg-hvQzNxwos4g5Jm2m .node ellipse,#mermaid-svg-hvQzNxwos4g5Jm2m .node polygon,#mermaid-svg-hvQzNxwos4g5Jm2m .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-hvQzNxwos4g5Jm2m .rough-node .label text,#mermaid-svg-hvQzNxwos4g5Jm2m .node .label text,#mermaid-svg-hvQzNxwos4g5Jm2m .image-shape .label,#mermaid-svg-hvQzNxwos4g5Jm2m .icon-shape .label{text-anchor:middle;}#mermaid-svg-hvQzNxwos4g5Jm2m .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-hvQzNxwos4g5Jm2m .rough-node .label,#mermaid-svg-hvQzNxwos4g5Jm2m .node .label,#mermaid-svg-hvQzNxwos4g5Jm2m .image-shape .label,#mermaid-svg-hvQzNxwos4g5Jm2m .icon-shape .label{text-align:center;}#mermaid-svg-hvQzNxwos4g5Jm2m .node.clickable{cursor:pointer;}#mermaid-svg-hvQzNxwos4g5Jm2m .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-hvQzNxwos4g5Jm2m .arrowheadPath{fill:#333333;}#mermaid-svg-hvQzNxwos4g5Jm2m .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-hvQzNxwos4g5Jm2m .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-hvQzNxwos4g5Jm2m .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-hvQzNxwos4g5Jm2m .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-hvQzNxwos4g5Jm2m .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-hvQzNxwos4g5Jm2m .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-hvQzNxwos4g5Jm2m .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-hvQzNxwos4g5Jm2m .cluster text{fill:#333;}#mermaid-svg-hvQzNxwos4g5Jm2m .cluster span{color:#333;}#mermaid-svg-hvQzNxwos4g5Jm2m div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-hvQzNxwos4g5Jm2m .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-hvQzNxwos4g5Jm2m rect.text{fill:none;stroke-width:0;}#mermaid-svg-hvQzNxwos4g5Jm2m .icon-shape,#mermaid-svg-hvQzNxwos4g5Jm2m .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-hvQzNxwos4g5Jm2m .icon-shape p,#mermaid-svg-hvQzNxwos4g5Jm2m .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-hvQzNxwos4g5Jm2m .icon-shape .label rect,#mermaid-svg-hvQzNxwos4g5Jm2m .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-hvQzNxwos4g5Jm2m .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-hvQzNxwos4g5Jm2m .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-hvQzNxwos4g5Jm2m :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} Users
读时模式
SET unmapped_fields='load'
字段从 _source 动态提取
FROM 子查询管道
索引A: 网关日志
WHERE status>=500
EVAL service='gateway'
索引B: 支付日志
WHERE transaction.status in ...
EVAL service='payments'
索引C: 认证日志
WHERE event.action='login'...
EVAL service='auth'
UNION ALL 合并
逻辑视图 (error_triage)
定义查询
存储于集群状态
Kibana Dashboard
Alerting
Ad-hoc Query
统一结果集
- 视图 将复杂的子查询封装为一个名字,消费者只需
FROM error_triage。 - 子查询 为每个索引独立定义过滤、计算和投影,最后以
UNION ALL合并。 - 读时模式 让子查询中的字段即使从未映射,也能从
_source中按需加载。
三者结合,你就能写出这样的查询:
esql
FROM error_triage
| STATS error_count = COUNT(*) BY url.path
| SORT error_count DESC
而 error_triage 的背后可能同时查询了网关、支付、认证三个服务的索引,并且用到了未映射的 url.path 字段------所有复杂性都被屏蔽在视图内部。
三、逻辑视图(Logical Views)------一次定义,处处复用
3.1 什么是逻辑视图?
逻辑视图本质上是一个命名的 ES|QL 查询,存储在 Elasticsearch 集群的元数据中。 它不存储数据,只存储查询定义。当你在 FROM 子句中引用视图名时,ES|QL 引擎会将其替换为视图对应的查询体,然后执行。
这就像数据库中的 CREATE VIEW,但更轻量,并且完全与 ES|QL 的管道语法融合。
3.2 如何创建和使用?
通过 _query/view API 创建:
http
PUT _query/view/error_triage
{
"query": """
FROM svc-gateway-*
| WHERE http.response.status_code >= 500
| KEEP @timestamp, http.response.status_code, url.path, source.ip
"""
}
然后你就可以在任何 ES|QL 查询中像使用普通索引一样使用它:
esql
FROM error_triage
| STATS error_count = COUNT(*) BY url.path
| SORT error_count DESC
3.3 原理与实现
#mermaid-svg-X9jsLfOUL01qC96f{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-X9jsLfOUL01qC96f .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-X9jsLfOUL01qC96f .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-X9jsLfOUL01qC96f .error-icon{fill:#552222;}#mermaid-svg-X9jsLfOUL01qC96f .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-X9jsLfOUL01qC96f .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-X9jsLfOUL01qC96f .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-X9jsLfOUL01qC96f .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-X9jsLfOUL01qC96f .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-X9jsLfOUL01qC96f .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-X9jsLfOUL01qC96f .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-X9jsLfOUL01qC96f .marker{fill:#333333;stroke:#333333;}#mermaid-svg-X9jsLfOUL01qC96f .marker.cross{stroke:#333333;}#mermaid-svg-X9jsLfOUL01qC96f svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-X9jsLfOUL01qC96f p{margin:0;}#mermaid-svg-X9jsLfOUL01qC96f .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-X9jsLfOUL01qC96f .cluster-label text{fill:#333;}#mermaid-svg-X9jsLfOUL01qC96f .cluster-label span{color:#333;}#mermaid-svg-X9jsLfOUL01qC96f .cluster-label span p{background-color:transparent;}#mermaid-svg-X9jsLfOUL01qC96f .label text,#mermaid-svg-X9jsLfOUL01qC96f span{fill:#333;color:#333;}#mermaid-svg-X9jsLfOUL01qC96f .node rect,#mermaid-svg-X9jsLfOUL01qC96f .node circle,#mermaid-svg-X9jsLfOUL01qC96f .node ellipse,#mermaid-svg-X9jsLfOUL01qC96f .node polygon,#mermaid-svg-X9jsLfOUL01qC96f .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-X9jsLfOUL01qC96f .rough-node .label text,#mermaid-svg-X9jsLfOUL01qC96f .node .label text,#mermaid-svg-X9jsLfOUL01qC96f .image-shape .label,#mermaid-svg-X9jsLfOUL01qC96f .icon-shape .label{text-anchor:middle;}#mermaid-svg-X9jsLfOUL01qC96f .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-X9jsLfOUL01qC96f .rough-node .label,#mermaid-svg-X9jsLfOUL01qC96f .node .label,#mermaid-svg-X9jsLfOUL01qC96f .image-shape .label,#mermaid-svg-X9jsLfOUL01qC96f .icon-shape .label{text-align:center;}#mermaid-svg-X9jsLfOUL01qC96f .node.clickable{cursor:pointer;}#mermaid-svg-X9jsLfOUL01qC96f .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-X9jsLfOUL01qC96f .arrowheadPath{fill:#333333;}#mermaid-svg-X9jsLfOUL01qC96f .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-X9jsLfOUL01qC96f .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-X9jsLfOUL01qC96f .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-X9jsLfOUL01qC96f .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-X9jsLfOUL01qC96f .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-X9jsLfOUL01qC96f .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-X9jsLfOUL01qC96f .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-X9jsLfOUL01qC96f .cluster text{fill:#333;}#mermaid-svg-X9jsLfOUL01qC96f .cluster span{color:#333;}#mermaid-svg-X9jsLfOUL01qC96f div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-X9jsLfOUL01qC96f .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-X9jsLfOUL01qC96f rect.text{fill:none;stroke-width:0;}#mermaid-svg-X9jsLfOUL01qC96f .icon-shape,#mermaid-svg-X9jsLfOUL01qC96f .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-X9jsLfOUL01qC96f .icon-shape p,#mermaid-svg-X9jsLfOUL01qC96f .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-X9jsLfOUL01qC96f .icon-shape .label rect,#mermaid-svg-X9jsLfOUL01qC96f .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-X9jsLfOUL01qC96f .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-X9jsLfOUL01qC96f .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-X9jsLfOUL01qC96f :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 用户查询: FROM error_triage ...
解析器识别视图名
查询集群状态中的视图定义
将视图查询体展开到 FROM 位置
优化器合并过滤条件,推送到底层索引
执行展开后的完整查询
- 存储:视图定义保存在集群状态(cluster state)中,由主节点维护,可跨集群搜索。
- 权限:支持独立的 RBAC 权限,可以控制哪些用户或角色能使用某个视图。
- 嵌套:视图可以引用其他视图,但要注意避免循环依赖。
- 自动更新:修改视图定义后,所有引用它的仪表盘、告警、查询立即生效,无需重新部署。
3.4 使用场景
- 统一错误排查入口:将多个服务的错误日志合并成一个视图,SRE 团队只需查询这一个视图。
- 安全脱敏 :在视图中
DROP掉敏感字段,再授权给非特权用户。 - 复杂业务指标:如"最近一小时活跃用户"的复杂计算,封装成视图供多个看板使用。
四、FROM 子查询(Subqueries)------异构索引的融合器
4.1 子查询
在生产环境中,不同微服务的日志往往存放在不同的索引中,它们的字段定义可能完全不同。比如:
- 网关日志有
http.response.status_code - 支付日志有
transaction.status - 认证日志有
event.action和event.outcome
要把它们合并成一个统一的"错误事件"列表,过去你需要在应用层查三次再合并,或者用 Elasticsearch 的 multi-index 但只能共享同一个过滤条件。现在,FROM 子查询允许你对每个索引独立编写管道 ,最后自动做 UNION ALL。
4.2 语法与示例
esql
FROM
(FROM svc-gateway-*
| WHERE http.response.status_code >= 500
| EVAL service = "gateway",
error_detail = CONCAT("HTTP ", http.response.status_code::string)
| KEEP @timestamp, service, error_detail, source.ip),
(FROM svc-payments-*
| WHERE transaction.status IN ("failed", "timeout")
| EVAL service = "payments",
error_detail = transaction.status
| KEEP @timestamp, service, error_detail, source.ip),
(FROM svc-auth-*
| WHERE event.action == "login" AND event.outcome == "failure"
| EVAL service = "auth",
error_detail = CONCAT(event.action, " ", event.outcome)
| KEEP @timestamp, service, error_detail, source.ip)
| SORT @timestamp DESC
| LIMIT 20
每个子查询都是一个独立的 ES|QL 管道,它们以逗号分隔,最终合并成一个结果流。后续的 SORT 和 LIMIT 作用在整个合并结果上。
4.3 内部执行流程
#mermaid-svg-0AKvUtEshqEbpTar{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-0AKvUtEshqEbpTar .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-0AKvUtEshqEbpTar .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-0AKvUtEshqEbpTar .error-icon{fill:#552222;}#mermaid-svg-0AKvUtEshqEbpTar .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-0AKvUtEshqEbpTar .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-0AKvUtEshqEbpTar .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-0AKvUtEshqEbpTar .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-0AKvUtEshqEbpTar .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-0AKvUtEshqEbpTar .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-0AKvUtEshqEbpTar .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-0AKvUtEshqEbpTar .marker{fill:#333333;stroke:#333333;}#mermaid-svg-0AKvUtEshqEbpTar .marker.cross{stroke:#333333;}#mermaid-svg-0AKvUtEshqEbpTar svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-0AKvUtEshqEbpTar p{margin:0;}#mermaid-svg-0AKvUtEshqEbpTar .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-0AKvUtEshqEbpTar .cluster-label text{fill:#333;}#mermaid-svg-0AKvUtEshqEbpTar .cluster-label span{color:#333;}#mermaid-svg-0AKvUtEshqEbpTar .cluster-label span p{background-color:transparent;}#mermaid-svg-0AKvUtEshqEbpTar .label text,#mermaid-svg-0AKvUtEshqEbpTar span{fill:#333;color:#333;}#mermaid-svg-0AKvUtEshqEbpTar .node rect,#mermaid-svg-0AKvUtEshqEbpTar .node circle,#mermaid-svg-0AKvUtEshqEbpTar .node ellipse,#mermaid-svg-0AKvUtEshqEbpTar .node polygon,#mermaid-svg-0AKvUtEshqEbpTar .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-0AKvUtEshqEbpTar .rough-node .label text,#mermaid-svg-0AKvUtEshqEbpTar .node .label text,#mermaid-svg-0AKvUtEshqEbpTar .image-shape .label,#mermaid-svg-0AKvUtEshqEbpTar .icon-shape .label{text-anchor:middle;}#mermaid-svg-0AKvUtEshqEbpTar .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-0AKvUtEshqEbpTar .rough-node .label,#mermaid-svg-0AKvUtEshqEbpTar .node .label,#mermaid-svg-0AKvUtEshqEbpTar .image-shape .label,#mermaid-svg-0AKvUtEshqEbpTar .icon-shape .label{text-align:center;}#mermaid-svg-0AKvUtEshqEbpTar .node.clickable{cursor:pointer;}#mermaid-svg-0AKvUtEshqEbpTar .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-0AKvUtEshqEbpTar .arrowheadPath{fill:#333333;}#mermaid-svg-0AKvUtEshqEbpTar .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-0AKvUtEshqEbpTar .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-0AKvUtEshqEbpTar .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-0AKvUtEshqEbpTar .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-0AKvUtEshqEbpTar .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-0AKvUtEshqEbpTar .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-0AKvUtEshqEbpTar .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-0AKvUtEshqEbpTar .cluster text{fill:#333;}#mermaid-svg-0AKvUtEshqEbpTar .cluster span{color:#333;}#mermaid-svg-0AKvUtEshqEbpTar div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-0AKvUtEshqEbpTar .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-0AKvUtEshqEbpTar rect.text{fill:none;stroke-width:0;}#mermaid-svg-0AKvUtEshqEbpTar .icon-shape,#mermaid-svg-0AKvUtEshqEbpTar .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-0AKvUtEshqEbpTar .icon-shape p,#mermaid-svg-0AKvUtEshqEbpTar .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-0AKvUtEshqEbpTar .icon-shape .label rect,#mermaid-svg-0AKvUtEshqEbpTar .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-0AKvUtEshqEbpTar .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-0AKvUtEshqEbpTar .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-0AKvUtEshqEbpTar :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} Branches
子查询1: 网关索引
WHERE/EVAL/KEEP
子查询2: 支付索引
WHERE/EVAL/KEEP
子查询3: 认证索引
WHERE/EVAL/KEEP
UNION ALL 合并
全局管道: SORT / LIMIT / STATS
最终结果
关键优化点:
- 过滤器下推 :优化器会将每个子查询中的
WHERE条件推送到对应的索引分片,充分利用索引过滤,减少扫描数据量。 - 并行执行:各子查询可以并行扫描不同的索引,最后在协调节点合并。
- 独立字段解析:每个子查询有自己的字段解析上下文,因此字段名可以不同,甚至同名字段类型不同时会在合并时做类型提升(如 integer 和 long 合并为 long)。
4.4 与视图的结合
子查询是视图的"建筑材料"------你可以定义一个视图,其查询体就是一段复杂的子查询组合,这样消费者完全感知不到底层的索引异构性。
五、读时模式(Schema-on-Read)------让遗漏的映射不再是噩梦
5.1 痛点回顾
Elasticsearch 是 schema-on-write 模式,索引数据前需要定义 mapping。如果后来发现漏了某个字段,传统做法是:
- 更新 mapping(但已索引的历史数据不会自动填充该字段)。
- 使用
_reindex将数据重新索引到新映射,对大数据量耗时且昂贵。
读时模式 打破了这一限制:你可以在查询时动态从 _source 中提取任意字段,即使它们从未出现在 mapping 中。
5.2 两种方式
方式一:SET unmapped_fields="load"(战略级抽象)
esql
SET unmapped_fields="load";
FROM otel-logs-*
| WHERE log.level IN ("error", "warn")
| STATS errors = COUNT(*), latest = MAX(@timestamp)
BY service.name, resource.cost_center
| SORT errors DESC
行为说明 :当这个开关开启后,查询中引用的任何字段,如果不在 mapping 中,ES|QL 会自动从 _source 中提取对应值。这让你可以"先上车后补票",先索引原始 JSON,后续随时查询任意嵌套字段。
方式二:JSON_EXTRACT(手术刀式精确提取)
esql
FROM my-index
| EVAL user_id = JSON_EXTRACT(json_field, "$.user.id")
| WHERE user_id = 123
JSON_EXTRACT 更底层,适合从存储为字符串的 JSON 或 flattened 字段中提取子字段。但 SET unmapped_fields="load" 使用起来更简洁,适合大多数场景。
5.3 内部原理:从 _source 动态加载
#mermaid-svg-fsZqNYHdvwvUEOAj{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-fsZqNYHdvwvUEOAj .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-fsZqNYHdvwvUEOAj .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-fsZqNYHdvwvUEOAj .error-icon{fill:#552222;}#mermaid-svg-fsZqNYHdvwvUEOAj .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-fsZqNYHdvwvUEOAj .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-fsZqNYHdvwvUEOAj .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-fsZqNYHdvwvUEOAj .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-fsZqNYHdvwvUEOAj .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-fsZqNYHdvwvUEOAj .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-fsZqNYHdvwvUEOAj .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-fsZqNYHdvwvUEOAj .marker{fill:#333333;stroke:#333333;}#mermaid-svg-fsZqNYHdvwvUEOAj .marker.cross{stroke:#333333;}#mermaid-svg-fsZqNYHdvwvUEOAj svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-fsZqNYHdvwvUEOAj p{margin:0;}#mermaid-svg-fsZqNYHdvwvUEOAj .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-fsZqNYHdvwvUEOAj .cluster-label text{fill:#333;}#mermaid-svg-fsZqNYHdvwvUEOAj .cluster-label span{color:#333;}#mermaid-svg-fsZqNYHdvwvUEOAj .cluster-label span p{background-color:transparent;}#mermaid-svg-fsZqNYHdvwvUEOAj .label text,#mermaid-svg-fsZqNYHdvwvUEOAj span{fill:#333;color:#333;}#mermaid-svg-fsZqNYHdvwvUEOAj .node rect,#mermaid-svg-fsZqNYHdvwvUEOAj .node circle,#mermaid-svg-fsZqNYHdvwvUEOAj .node ellipse,#mermaid-svg-fsZqNYHdvwvUEOAj .node polygon,#mermaid-svg-fsZqNYHdvwvUEOAj .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-fsZqNYHdvwvUEOAj .rough-node .label text,#mermaid-svg-fsZqNYHdvwvUEOAj .node .label text,#mermaid-svg-fsZqNYHdvwvUEOAj .image-shape .label,#mermaid-svg-fsZqNYHdvwvUEOAj .icon-shape .label{text-anchor:middle;}#mermaid-svg-fsZqNYHdvwvUEOAj .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-fsZqNYHdvwvUEOAj .rough-node .label,#mermaid-svg-fsZqNYHdvwvUEOAj .node .label,#mermaid-svg-fsZqNYHdvwvUEOAj .image-shape .label,#mermaid-svg-fsZqNYHdvwvUEOAj .icon-shape .label{text-align:center;}#mermaid-svg-fsZqNYHdvwvUEOAj .node.clickable{cursor:pointer;}#mermaid-svg-fsZqNYHdvwvUEOAj .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-fsZqNYHdvwvUEOAj .arrowheadPath{fill:#333333;}#mermaid-svg-fsZqNYHdvwvUEOAj .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-fsZqNYHdvwvUEOAj .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-fsZqNYHdvwvUEOAj .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-fsZqNYHdvwvUEOAj .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-fsZqNYHdvwvUEOAj .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-fsZqNYHdvwvUEOAj .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-fsZqNYHdvwvUEOAj .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-fsZqNYHdvwvUEOAj .cluster text{fill:#333;}#mermaid-svg-fsZqNYHdvwvUEOAj .cluster span{color:#333;}#mermaid-svg-fsZqNYHdvwvUEOAj div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-fsZqNYHdvwvUEOAj .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-fsZqNYHdvwvUEOAj rect.text{fill:none;stroke-width:0;}#mermaid-svg-fsZqNYHdvwvUEOAj .icon-shape,#mermaid-svg-fsZqNYHdvwvUEOAj .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-fsZqNYHdvwvUEOAj .icon-shape p,#mermaid-svg-fsZqNYHdvwvUEOAj .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-fsZqNYHdvwvUEOAj .icon-shape .label rect,#mermaid-svg-fsZqNYHdvwvUEOAj .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-fsZqNYHdvwvUEOAj .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-fsZqNYHdvwvUEOAj .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-fsZqNYHdvwvUEOAj :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 是
否
nullify
load
查询引用字段 X
字段在 mapping 中?
正常从列存(doc_values)读取
unmapped_fields 设置?
返回 null
从 _source 解析该字段
缓存在查询上下文中
返回字段值
- 当
unmapped_fields="load"时,ES|QL 解析器会检查每个引用字段是否在映射中,若不在,则标记为"需要从_source加载"。 - 执行阶段,对于每个文档,系统只从
_source中提取查询中实际用到的未映射字段,而不是加载整个_source,因此性能开销相对可控。 - 提取后的值会参与后续所有管道操作,和普通映射字段的行为完全一致。
注意 :读时模式目前是 Tech Preview,建议在测试环境充分验证性能表现。对于高频查询,后续路线图中会提供"原生扁平字段支持",进一步优化性能。
5.4 使用建议
- 适用场景:日志分析、探索式查询、字段经常变动的业务。
- 不适用场景 :对性能要求极苛刻的实时核心交易查询(因为
_source读取比列存慢)。 - 最佳实践:读时模式和传统映射可以共存,你可以只针对少数漏配字段使用读时模式,大部分字段仍用映射。
六、时区支持(GA)------让时间真正属于你
之前 ES|QL 中的日期操作默认使用 UTC 时间,你必须在应用层做时区转换。现在 SET time_zone 正式 GA,一次设置,所有时间函数均按指定时区处理。
esql
SET time_zone="Asia/Shanghai";
FROM error_triage
| EVAL local_hour = DATE_TRUNC(1 hour, @timestamp)
| STATS errors = COUNT(*) BY local_hour, service
| SORT local_hour DESC
原理 :时区信息会传递给 DATE_TRUNC、日期直方图聚合以及最终时间戳的格式化输出。底层存储的 @timestamp 依然是 UTC 毫秒值,但在计算和展示时通过时区偏移量调整,无需修改数据。
支持所有 IANA 时区,如
America/New_York、Europe/London等。
七、LIMIT BY------原生分组 Top-N
以往要获取"每个服务的前3个错误类型",你需要先全局排序,再用应用代码或复杂的脚本过滤。现在一条命令搞定:
esql
FROM error_triage
| STATS cnt = COUNT(*) BY service, error_detail
| SORT cnt DESC
| LIMIT 3 BY service
执行逻辑:
#mermaid-svg-ALpNcIh4vryVFSHv{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-ALpNcIh4vryVFSHv .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-ALpNcIh4vryVFSHv .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-ALpNcIh4vryVFSHv .error-icon{fill:#552222;}#mermaid-svg-ALpNcIh4vryVFSHv .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-ALpNcIh4vryVFSHv .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-ALpNcIh4vryVFSHv .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-ALpNcIh4vryVFSHv .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-ALpNcIh4vryVFSHv .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-ALpNcIh4vryVFSHv .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-ALpNcIh4vryVFSHv .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-ALpNcIh4vryVFSHv .marker{fill:#333333;stroke:#333333;}#mermaid-svg-ALpNcIh4vryVFSHv .marker.cross{stroke:#333333;}#mermaid-svg-ALpNcIh4vryVFSHv svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-ALpNcIh4vryVFSHv p{margin:0;}#mermaid-svg-ALpNcIh4vryVFSHv .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-ALpNcIh4vryVFSHv .cluster-label text{fill:#333;}#mermaid-svg-ALpNcIh4vryVFSHv .cluster-label span{color:#333;}#mermaid-svg-ALpNcIh4vryVFSHv .cluster-label span p{background-color:transparent;}#mermaid-svg-ALpNcIh4vryVFSHv .label text,#mermaid-svg-ALpNcIh4vryVFSHv span{fill:#333;color:#333;}#mermaid-svg-ALpNcIh4vryVFSHv .node rect,#mermaid-svg-ALpNcIh4vryVFSHv .node circle,#mermaid-svg-ALpNcIh4vryVFSHv .node ellipse,#mermaid-svg-ALpNcIh4vryVFSHv .node polygon,#mermaid-svg-ALpNcIh4vryVFSHv .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-ALpNcIh4vryVFSHv .rough-node .label text,#mermaid-svg-ALpNcIh4vryVFSHv .node .label text,#mermaid-svg-ALpNcIh4vryVFSHv .image-shape .label,#mermaid-svg-ALpNcIh4vryVFSHv .icon-shape .label{text-anchor:middle;}#mermaid-svg-ALpNcIh4vryVFSHv .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-ALpNcIh4vryVFSHv .rough-node .label,#mermaid-svg-ALpNcIh4vryVFSHv .node .label,#mermaid-svg-ALpNcIh4vryVFSHv .image-shape .label,#mermaid-svg-ALpNcIh4vryVFSHv .icon-shape .label{text-align:center;}#mermaid-svg-ALpNcIh4vryVFSHv .node.clickable{cursor:pointer;}#mermaid-svg-ALpNcIh4vryVFSHv .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-ALpNcIh4vryVFSHv .arrowheadPath{fill:#333333;}#mermaid-svg-ALpNcIh4vryVFSHv .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-ALpNcIh4vryVFSHv .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-ALpNcIh4vryVFSHv .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-ALpNcIh4vryVFSHv .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-ALpNcIh4vryVFSHv .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-ALpNcIh4vryVFSHv .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-ALpNcIh4vryVFSHv .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-ALpNcIh4vryVFSHv .cluster text{fill:#333;}#mermaid-svg-ALpNcIh4vryVFSHv .cluster span{color:#333;}#mermaid-svg-ALpNcIh4vryVFSHv div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-ALpNcIh4vryVFSHv .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-ALpNcIh4vryVFSHv rect.text{fill:none;stroke-width:0;}#mermaid-svg-ALpNcIh4vryVFSHv .icon-shape,#mermaid-svg-ALpNcIh4vryVFSHv .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-ALpNcIh4vryVFSHv .icon-shape p,#mermaid-svg-ALpNcIh4vryVFSHv .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-ALpNcIh4vryVFSHv .icon-shape .label rect,#mermaid-svg-ALpNcIh4vryVFSHv .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-ALpNcIh4vryVFSHv .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-ALpNcIh4vryVFSHv .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-ALpNcIh4vryVFSHv :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 分组聚合结果
按 service 分组
每组内取 cnt 最大的 3 行
返回每组 TopN
LIMIT N BY field会在每个field的取值组内保留前 N 条记录(基于前面的SORT顺序)。- 如果某个分组不足 N 条,则返回该组所有记录。
- 该操作在协调节点完成,适合分组数不太多(如几百以内)的场景。
八、FIRST/LAST/EARLIEST/LATEST(GA)------时间序列取值更简单
在时间序列分析中,经常需要取一组记录中最早或最晚的某个字段值。
FIRST(value_field, sort_field):按sort_field升序排列后,取第一条记录的value_field。LAST(value_field, sort_field):类似,取最后一条。EARLIEST(value_field):等同于FIRST(value_field, @timestamp),默认按时间戳排序。LATEST(value_field):等同于LAST(value_field, @timestamp)。
示例:
esql
FROM svc-auth-*
| WHERE event.outcome == "failure"
| STATS first_seen = FIRST(@timestamp, @timestamp),
last_seen = LAST(@timestamp, @timestamp),
first_user = EARLIEST(user.name),
attempts = COUNT(*)
BY source.ip
| SORT attempts DESC
这会按 source.ip 分组,找出每个 IP 首次和最后一次失败的时间,以及首次失败的用户名。
九、URI_PARTS、USER_AGENT、REGISTERED_DOMAIN------结构化解析管道
这三个新管道命令将常见的字符串解析工作集成到 ES|QL 中,省去在应用层解析的麻烦。
URI_PARTS
将 URL 拆解为协议、域名、路径、端口等:
esql
FROM svc-gateway-*
| URI_PARTS parts = url.full
| STATS errors = COUNT(*) BY parts.domain, parts.path
输出字段:parts.scheme、parts.domain、parts.path、parts.port、parts.query 等。
USER_AGENT
解析 User-Agent 字符串,得到浏览器、操作系统、设备信息:
esql
FROM svc-auth-*
| USER_AGENT ua = user_agent.original
| STATS cnt = COUNT(*) BY ua.name, ua.version, ua.os.name
REGISTERED_DOMAIN
从域名中提取注册域和顶级域,用于分析访问来源的域名分布:
esql
FROM access-logs
| REGISTERED_DOMAIN rd = hostname
| STATS visits = COUNT(*) BY rd.registered_domain, rd.top_level_domain
十、LOOKUP JOIN 性能优化
ES|QL 在 9.1 引入了 LOOKUP JOIN,用于将主查询与一个较小的索引做左连接,实现数据富化。本次升级带来了两项性能优化:
-
Lucene 结构复用
当多次执行相同查询(不同参数)查询同一个 lookup 索引时,系统会缓存该索引的
doc_values和TermsEnum数据结构,避免重复打开文件和构建字典。这对典型场景------如"每条日志查找用户信息表"------效果显著,因为 lookup 索引通常远小于主表。 -
单关键字 join 快速路径
如果 join 条件是一个单字段(最常见的情况),执行器会采用更轻量的哈希查找,减少每行处理开销。
优化后,LOOKUP JOIN 在高并发富化场景下性能提升可达数倍。
本地化部署与实战建议
Elasticsearch 的本地化部署需要注意:
- 时区设置 :建议默认使用
Asia/Shanghai,避免每次查询手动设置。 - 索引命名规范 :统一采用
服务名-日期模式,便于子查询使用通配符(如svc-*-*)。 - 读时模式慎用 :尽管方便,但频繁从
_source读取大字段可能拖慢查询。建议先用SET unmapped_fields="nullify"测试查询逻辑,确认只涉及少量未映射字段时再启用load。 - 视图权限控制:利用视图的 RBAC,可以为不同团队提供定制化的数据视角,减少误操作风险。
- Kibana 集成:在 Kibana 的 ES|QL 实验室中尝试新语法,逐步迁移现有查询到视图和子查询模式。
结语
Elasticsearch ES|QL 的这次更新,不仅仅是增加了几个关键字,而是从根本上改变了我们与数据交互的方式:
- 视图 让逻辑复用成为一等公民;
- 子查询 让异构数据源融合变得优雅;
- 读时模式 让 schema 演进不再是负担;
- 时区、LIMIT BY、LOOKUP 优化 则让日常查询更加顺手。
本文基于 Elasticsearch 9.x 版本,所有特性 API 以官方最新文档为准。