高效的 Kibana 仪表板

2025年3月11日   |   by mebius

作者:来自 ElasticThomas Neirynck

%title插图%num

从 8.13 到 8.17,数据出现在仪表板上的等待时间提高了 40%。这些改进在我们的综合基准测试环境和真实用户云环境中收集的指标中都得到了验证。

几乎所有 Elastic tack 用户在某些时候都会使用仪表板。 Elastic 为其所有集成提供了许多开箱即用的仪表板,用户可以创建自定义仪表板:与他人共享、进行根本原因分析和/或生成 PNG 报告。因此,仪表板应用程序是 Kibana 中最常用的应用程序之一(对于那些好奇的人来说,另一个最受欢迎的应用程序是 Discover)。

此外,Dashboards 的许多核心应用程序组件都支持其他 Kibana 应用程序tgcode。 Kibana 用于嵌入图表和表格的小部件框架最初是为仪表板或数据插件开发的,它将搜索请求从 Kibana 浏览器应用程序代理到 Elasticsearch,并被所有 Kibana 插件大量使用。

换句话说,无论从产品还是技术角度来看,仪表板应用程序都是 Kibana 的核心,值得为用户提供最佳体验。

然而,Kibana 中的仪表板可能会感觉 “迟缓”。我们会以开发人员的身份体验它,也会从用户那里听到它。

与其他工具(如 Gratgcodefana,或在较小程度上是 OpenSearch Dashboards)的比较也表明,Kibana 中的仪表板有时会感觉更慢。

为此,Kibana 团队最近致力于缩短仪表板的渲染时间。

识别挑战

查看真实用户遥测(我们将在下面进一步讨论这一点),我们看到仪表板的渲染时间呈现出明显的 80/20 划分。

a)一方面,一些仪表盘需要几十秒才能加载

这里的时间主要由(非常)长时间运行的 Elasticsearch 查询所占据。仪表板渲染时间的第 95 个百分位数(即少数)明显高于平均值。这本来并不令人意外;例如,搜索面板问题可能跨越很大的时间范围,查询触及未针对分析工作负载进行优化的冷存储层或冻结存储层。这些是少数仪表板,即 “长尾”。

%title插图%num

还要注意数据中明显的季节性,工作日的渲染时间比周末更长。这可能表明 “最坏情况” 受到集群上的整体负载(包括摄取(而不仅仅是运行时分析查询))的影响,而该负载往往会在工作时间内升高。

b) 另一方面,大多数仪表板在低秒范围内加载。

75% 及以下(绿线、蓝线和红线)明显较少,但仍然需要 1-3 秒。

当 Elasticsearch 中的搜索快速完成时,Kibana 中的时间都去哪儿了?为什么 Kibana 仪表板会感觉迟缓,特别是在部署良好且搜索快速完成的情况下?

在项目的初始阶段(我们将在这篇博文中总结),我们决定在 2024 年春季专注于改进 (b):第 75 个百分位仪表板的渲染时间,并确保这些仪表板变得更加敏捷和令人愉快。

我们没有忘记(a)!在文章的最后,我们将重点介绍那些可以缩短前 20% 的渲染时间的举措。

遥测

从一开始,我们就意识到我们对仪表板的渲染时间的测量很不准确。现有的仪器没有捕捉仪表板页面加载的各个阶段。将指标与我们在日常开发中可以采取的行动以及我们可以从现实世界的用户那里收集到的信息相结合也很重要。

a)测量什么

从高层次来看,我们引入了三个核心指标,每个指标捕获仪表板负载的特定跨度。当在新选项卡中通过其唯一的 URL 打开仪表板时,这些内容可以从下到上整齐地堆叠。

Metric What When Frequency
kibana_loaded 初始资产加载会话(“旋转器”) 首次打开 Kibana 每个用户会话一次
dashboard_overhead 仪表板应用程序和面板的引导 打开特定仪表板 每个仪表板一次
time_to_data 数据出现在屏幕上的时间 更改过滤器、时间范围、控制…… 每次查询状态改变一次

这种监测大部分是临时实施的。传统的核心网络生命力(https://web.dev/articles/vitals)并不能准确衡量我们所寻找的内容,尽管我们可以得出一些相似之处。

具体来说,交互时间(Time-To-Interactive – TTI)大致对应于 “kibana_loaded” 的结束。此后,应用程序即可使用,尽管并非所有 UX 可能都已完全初始化。例如,即使仪表板应用程序在技术上可以开始响应输入,但仪表板控件可能尚未初始化。

另一个棘手的指标是将仪表板的“完成”状态与所有数据都显示在屏幕上的时刻相匹配,这类似于最大内容绘制(Largest Contentful Paint – LCP)的概念。这个过程涉及 AJAX 数据请求和前端渲染,而这些都是针对每种面板类型定制的。

因此,为了正确地收集这个指标,仪表板上的每个面板都需要报告它的 “完成” 状态。

对于某些图表来说,这相当简单(例如,简单的度量图表),但对于其他图表来说,这很复杂。例如,只有当所有图块都呈现在屏幕上时,地图才算完整。检测这一点并不是一件容易的事。

在所有面板类型正确报告 “完成” 之后,仪表板应用程序本身将观察所有这些 “完成” 事件,并在最后一次完成后相应地报告 “数据时间” 指标。

此外,还引入了这些基准的进一步细分,以及额外的元数据,例如仪表板上的面板数量,或者用户是从 Kibana 内部导航(某些资产已加载)还是从 Kibana 外部导航到仪表板(尚未加载任何资产)。该应用程序还在服务器上收集数据请求持续时间的指标。

b) 每个阶段的重要性

这些阶段各自代表不同的含义。按顺序自上而下来看每个指标:

当谈到 “响应速度”(snappyness)时,它主要受到time-2-data的影响。每当用户调整过滤条件(例如时间范围),他们都需要等待新的数据出现在屏幕上。这就是 “延迟” 最关键的地方。可以把它想象成一款视频游戏。玩家或许可以忍受关卡加载前的加载图标,但一旦进入游戏,他们就期望操作能够非常流畅和即时反馈。

仪表板也是同样的道理。用户与图表、控件、筛选条件等交互时,这些交互就是仪表板的 “游戏体验”,而这些交互的顺畅度决定了用户对仪表板响应速度的整体感受。

%title插图%num

Dashboard_overhead 也是一个重要的指标。它表示加载一个仪表板配置所需的时间(本质上是从 Elasticsearch 的系统索引中检索一个文档)。除此之外,它还包括一些额外的代码加载时间。这是因为在 Kibana 的插件系统中,部分代码是按需加载的。

举个例子:假设某个仪表板上有一个 swimlane 面板。当仪表板应用初始化这个 “swimlane” 嵌入组件时,如果这是当前 Kibana 会话中第一次加载 swimlane 组件,那么这个 swimlane 嵌入组件就需要确保在渲染之前加载所有 swimlane 相关的代码。tgcode

深入探讨 Kibana 插件系统的机制可能会超出范围,但总结来说:dashboard_overhead不仅仅是 “kibana_loaded” 这个阶段,还包括了一些面板级别的代码加载开销。

kibana_loaded 只在整个Kibana用户会话期间发生一次,并且与特定仪表板无关。用户可能从 Kibana 的其他页面导航到仪表板页面。因此,我们希望把kibana_loaded这个指标与仪表板的具体体验隔离开来。虽然kibana_loaded往往是整个加载流程中耗时最长的部分,但从 Kibana 整体的 “响应速度”(snappyness)角度来看,它其实是最不重要的。

基准测试

在有了相应的监测机制之后,我们接下来需要在合适的环境中收集这些性能指标:既包括基准测试环境,也包括真实用户部署环境

a) 在CI环境中

我们会针对几套具有代表性的仪表板收集性能指标。这些仪表板的配置与我们的集成环境相似,每个仪表板都包含多种类型的图表组合。

%title插图%num
仪表板基准测试示例

这些基准测试每隔三小时在 Kibana 的主发布分支上运行一次。测试脚本会启动一个专用硬件上的 Elasticsearch 和 Kibana 集群。性能指标是通过 Playwright 脚本,在无头模式(headless)的 Chromium 浏览器中收集的。

b) 真实环境中的数据

同样的性能指标也会从Elastic CloudServerless用户那里收集,对于自托管的用户,如果他们选择加入遥测(telemetry),我们也会收集这些数据。

虽然 CI 环境下的基准测试提供了实时的、可操作的信号,但真实环境中的数据则是回顾性的信号,帮助我们验证基准测试的趋势是否真实反映在了实际用户的体验中。

在这篇文章的后续部分,你还会看到,这两个数据来源在过去一年中是如何共同演进和改善的。

关于流程的一点说明(会议!报警!)

没有某个特定的工程师或团队单独 “负责” 整个仪表板体验,因为各类展示面板是由不同工程团队分别开发的。

为了让各方协同推进,一些流程上的安排已经被证明是非常有效的。

报告机制

每周两次的报告会议,提供了回顾遥测数据变化的机会,并且可以讨论正在进行的相关工作。这些会议其实可以看作是小型的回顾会(mini-retrospectives)。

回应回归问题

基准测试的稳定性和重现性都非常高,因此当基准测试中出现负面或异常的趋势时,通常可以可靠地认为是性能回退(regression),需要及时处理。

具体如何响应,没有一刀切的规则:

  • 如果是严重回退,通常会立刻回滚
  • 其他情况下,也可能暂时保留有问题的提交,等补丁修复合并后再解决。

具体怎么处理是逐案判断的,取决于代码变更的性质和影响范围。

%title插图%num
我们的基准测试中的典型微笑模式。检测到并解决了回归问题。

临时运行 – 验证假设

随着大家对这套工具越来越熟悉,我们现在也会在某些特定 PR 合并之前,触发一些临时性能测试

这样可以在代码合并到主分支之前,更快速地验证其对性能的影响。

改进总结

在把这些 “琐碎” 工作(yak-shaving)理顺之后,终于可以聊聊真正有意思的部分了。

这里没有什么银弹。Kibana 的性能瓶颈分布在各个环节,每个环节的细微开销累加起来,最终影响整体表现。

仪表板渲染性能的提升,主要是来自应用各层的基础清理和优化。下面是几个核心方向:

减少代码和资源加载

高效的代码加载是 Kibana 当前面临的最大挑战之一。Kibana 的插件架构非常灵活,这种灵活性带来了快速添加新页面和新应用的能力。

但这种灵活性也是有代价的,尤其是对浏览器端的 JavaScript 加载影响很大,主要体现在两个方面:

  • 加载了很多其实不需要的代码
  • 资源加载非常碎片化,一个页面可能要加载很多小的cJavaScript 文件,而不是少量几个大文件

对于仪表板来说,第一个问题尤其明显。比如,很多插件都会给仪表板应用注册自己的小部件,像地图面板(maps panel)、泳道面板(swimlane panel)等等。然而,大部分仪表板根本用不到这些面板,但相关代码仍然可能被加载。

比如,插件还可以给仪表板面板添加上下文弹出链接,这些链接是上下文依赖的,但相关的代码也可能无差别地加载。

%title插图%num

在之前:插件如何重新配置​​仪表板面板的弹出项目的伪代码。此模式导致加载不必要的代码。

import { isCompatible } from './is_compatible'; //BAD. LOADED ON PAGE LOAD, EVEN WHEN NEVER NEEDED

export class MyPlugin {
  public start(core, plugins) {
    plugins.uiActions.addTriggerAction(CONTEXT_MENU_TRIGGER, {
      id: 'case_action',
      isCompatible: async (context) => {
        return isCompatible(conext);
      }, // ... OTHER API METHODS ...
    }); //BAD. ENTIRE IMPLEMENTATION IS INCLUDED IN PAGE LOAD
  }
}

为了解决这个问题,我们引入了一种新的模式,允许客户端延迟加载代码,直到真正需要时才去加载。

优化后

  • 代码只有在真正需要时才会被加载,不再提前加载所有可能的依赖。
  • 相关逻辑和定义被隔离到单独的模块中,避免和核心逻辑耦合。

这种模式不仅减少了初始加载时间,也让整个代码加载过程更加精细化和可控,从而提升了仪表板的整体性能。

./case_cation.ts

import { isCompatible } from './is_compatible';

export const caseAction = {
  isCompatible: async (context) => {
    return isCompatible(context);
  }, // ... OTHER API METHODS...
};

./plugin.ts

export class MyPlugin {
  public start(core, plugins) {
    plugins.uiActions.addTriggerAction(CONTEXT_MENU_TRIGGER, 'case_action', async () => {
      // GOOD. LET THE REGISTRY DECIDE WHEN TO LOAD THE DEFINITION
      return import('./case_action'); //GOOD. ACTION IMPLEMENTATION ONLY LOADED WHEN NEEDED.
    });
  }
}

另一个问题是:插件初始化会阻塞页面的整体响应速度,导致资源加载呈现瀑布式的顺序加载,而不是并行加载

示例

一个典型例子是,仪表板控件(dashboard controls)曾经会阻塞整个页面的渲染。所有面板必须等到这些控件加载完它们的资源后,才能开始渲染。

其实这完全没有必要 —— 面板的渲染完全可以在控件还没初始化完之前就开始

取得的改进

针对这些代码加载问题,团队已经做了不少优化,这些改进直接提升了仪表板的整体响应速度

不过,由于 Kibana 的插件数量已经超过200 个(而且还在不断增加),想要彻底解决这些低效的代码加载模式,仍然需要持续关注和优化。

嵌入式组件和渲染

2024年,Kibana 进行了嵌入式组件重构(embeddable refactor),这一努力有几个目标,其中性能虽然是一个附带考虑,但并非唯一目标。重构的关键目的是:

  • 启用一些关键功能(如可折叠面板)。
  • 移除许多未使用的代码路径(主要是与 Angular 相关的部分)。
  • 提高可测试性。
  • 简化开发者体验(DevX),尤其是在使用 API 时。

如果想深入了解,在这里可以阅读更多相关内容。

嵌入式组件提升性能的方式

通过将所有面板的渲染集中在单一的 React 树中,嵌入式组件在提升仪表板性能方面发挥了重要作用。
在此之前,每个面板都使用ReactDOM.render() 渲染到自己的渲染树中。这种架构源于一个 Kibana 同时使用 Angular 和 React(以及 jQuery,ahem)的时代。那时,Kibana 采用了多种渲染技术。但自 4 年前开始,Kibana 已经完全统一使用 React 作为唯一的 UI 渲染库,其他渲染技术被淘汰。然而,仪表板依然保留了一个额外的抽象层,延续了这一混合的架构。

其他优化

  • 减少面板响应的状态变化和重新渲染,整体上提高了仪表板的响应速度。
  • 减少代码量也有助于减小应用的负担和内存占用。

这些改进共同作用,极大地提升了仪表板的性能和响应性。

避免不必要的内存分配

在 Kibana 中,图表和表格的代码会重新组织从 Elasticsearch 接收到的数据,使其更易于操作和展示。这个过程中会执行 “扁平化” 操作,将嵌套的数据结构转化为一维数组,其中数组的每一项对应一个特征(例如,表格中的一行,柱状图中的一条柱子等)。

例如,考虑一个包含多个子字段的嵌套 ES 文档,或者是一个 ES 聚合搜索中的层次化桶组织。执行这些扁平化操作时,常常会分配短生命周期的对象,比如对象字面量或者箭头函数(() => {})。频繁使用数组推导方法,如 .map.reduce,很容易引入这种不必要的内存分配。

问题产生的原因

这些扁平化操作通常发生在紧密的递归循环中(例如,处理数千个文档或数百个桶)。由于仪表板可能包含多个表格和图表,这些内存分配会迅速累积。这种过度的堆内存分配对用户体验造成双重影响:

  1. 构建过程的性能问题,
  2. 垃圾回收压力,垃圾回收往往是不可预测的,但会导致帧率的波动和卡顿。

改进措施

通过移除一些最明显的内存分配,尤其是在这些紧密循环中,我们的基准测试显示了5-10% 的性能提升。这个优化不仅减少了内存压力,还帮助减少了垃圾回收的频率,从而改善了整体用户体验。

数据传输改进

在 Kibana 浏览器运行的仪表板与 Elasticsearch 之间的数据请求往返过程中,采用了批处理方式。多个请求会被收集起来,然后 Kibana 服务器将这些请求作为独立的_async_search请求分发给 Elasticsearch,并将返回的 ES-JSON 响应组合成一种新的 Kibana 特定格式。

批处理的主要原因

批处理的主要原因是为了绕过浏览器在HTTP1中的连接限制,这个限制大约是6个并发 HTTP 请求。而在仪表板包含多个面板时,这个限制很容易被超出。

批处理的缺点

尽管批处理解决了连接限制问题,但也有两个主要缺点:

  1. 收集批处理的延迟:为了批量收集请求,会引入一些延迟。
  2. 给 Kibana 服务器带来负担:Kibana 服务器需要等待并重新编码 ES 响应。具体过程是:

    • 解压缩响应
    • 解码 JSON 数据
    • 合并响应
    • 再次进行 gzip 压缩

尽管这种重新编码的过程通常是小规模的,但在一些极端情况下(例如,对于大型响应),可能会变得显著,并且增加了内存压力,偶尔还会导致**内存溢出(Out Of Memory)**问题。

%title插图%num

数据传输改进:去除批处理

考虑到 Elastic Cloud 和 Serverless 前面的代理已经支持HTTP 2.0,并且 Kibana 将在9.0 版本开始支持 HTTP 2.0 的有状态请求(stateful),因此决定移除批处理机制。此外,Kibana 服务器不再重新编码数据,而是直接从 Elasticsearch 流式传输原始的gzip 压缩响应

改进带来的好处

  • 简化了数据传输架构:去除了中间的批处理和重新编码步骤,架构变得更加简洁。
  • 性能提升:在 HTTP 2.0 的支持下,传输速度更快,且更不容易受到内存溢出(OOM)问题的影响。
  • 更易调试:简化后的架构使得数据响应更加直观,且可以轻松地在浏览器调试工具中查看和检查数据响应。

总体来说,这些改进不仅提升了性能,还增强了系统的稳定性可调试性

%title插图%num

成效

这些变更的综合结果非常显著,反映在基准测试用户遥测数据中。

基准测试演变

下图展示了多个基准测试混合后的指标变化,以及它们在过去6个月中的演变情况。我们可以看到,总体响应时间从大约3500ms下降到2000ms

:最近的一次上升与当前对 Kibana 主题进行更改的工作有关。在这个迁移阶段,我们同时发布了多个主题。这个问题会随着时间推移逐步解决。图中还有一些空白,是因为 CI 运行出现了故障。

这种基准的变化清晰展示了优化措施在性能上的实际效果。

%title插图%num

现实世界用户

如引言中所述,现实世界的数据更难衡量。我们无法确切知道用户正在使用哪些仪表板,以及他们的配置是如何随时间变化的。

然而,从两个不同的角度来看,我们能够验证在合成基准测试环境中观察到的相同演变趋势。

首先,随着时间的推移,我们在75百分位数的渲染时间出现了下降。这使我们能够得出结论 ——2025年1月的用户仪表板体验显著优于2024年6月,也就是说,用户的仪表板渲染速度更快。

仪表板渲染时间(25、50、75百分位数)

%title插图%num

在上周,我们还可以按所有用户的版本比较平均 time_to_data。 8.17 中的用户等待数据出现在屏幕上的时间比 8.12 的用户更少。

%title插图%num

展望未来

当前的趋势呈下降态势,主要通过进行许多小的优化来实现。

然而,有一些重要领域,使用这种精简脂肪的方法最终将导致收益递减。以下是我们认为在如何解决问题时,可能需要进行更结构性变化的一些领域。

继续改进代码加载

在这篇博客中,我们没有过多讨论 kibana_loaded 指标。如果我们要对它进行概括:Kibana 的插件架构优化是为了允许应用程序按需加载代码,且代码打包过程会生成许多 JavaScript 代码包。然而,实际上我们确实看到了一些不必要的代码被加载,以及 “瀑布式” 的代码加载,这可能会阻塞用户体验的渲染。总体来说,这个方面还有很大的改进空间(参见前文中的 “减少代码和资源加载” 部分)。

团队目前正在进行一个更广泛的工作 ——“可持续 Kibana”,其中包括重新审视如何将代码打包并交付到浏览器。

我们预计在这方面会有更多的收益,届时欢迎查看博客更新!

解决慢查询问题

慢查询就是慢查询。Elasticsearch 的查询可能会因多种原因变慢,这甚至不一定是一个 bug。考虑到在大规模集群中进行复杂的聚合查询,可能涉及数百个节点和不同存储层的交互,特别是在时间跨度较长并且数据量达到TB 级别的情况下,这种查询的慢速可能是不可避免的。

在这种情况下,我们不能通过对 Kibana 的低级别改进(如上述讨论的优化)来改善仪表板的 “响应速度”。要解决这种固有的慢速问题,需要一系列新功能,让用户能够选择 “快速模式”。

例如,用户可以选择采样数据,在速度和准确性之间做权衡;或者通过增量填充图表来改善感知性能;又或者允许用户使用可折叠面板隐藏部分仪表板(这一功能正在开发中)。

这些变化将更多地跨越产品功能低级技术改进之间的界限。

图表和查询相关的迟缓问题

目前的工作主要关注于改善具有广泛影响的低级组件。然而,仍然有一些情况,仪表板的慢速是由特定的图表配置引起的。

例如:

  • 计算 “其他” 桶(“Other” bucket)
  • 高基数数据上的唯一计数聚合

识别这些图表和查询将有助于进行更有针对性的优化。例如:默认设置是否合理?(例如,所有图表是否都需要另一个桶?)是否有更高效的方式查询相同的数据?

将 Discover 加入该计划

仪表板中的所有痛点,在 Discover 中也是相同的痛点(图表的插件化、数据繁重、响应性要求等)。因此,我们将这个计划扩展到了Discover 应用的开发中。

我们已经在 Discover 中看到了不错的改进,并希望在此基础上继续推动发展。这也值得单独撰写一篇博客,所以请继续关注!

结论

Kibana中的仪表板正在变得更加快速。最近的改进是许多低级优化的复合效应结果。

为了进一步提升性能,我们预期将采用双管齐下的方法:

  1. 继续加强优化基础,保持改进的持续性。
  2. 扩展到一个更广泛的计划,解决导致性能变慢的 “长尾” 问题。

Elasticsearch不断推出新功能,帮助你根据具体用例构建最佳的搜索解决方案。深入了解我们的示例笔记本,开始免费云试用,或立即在本地机器上尝试Elastic。

原文:Fast Kibana Dashboards – Elasticsearch Labs

文章来源于互联网:高效的 Kibana 仪表板