每个监视系统都面临三种类型的性能问题。
首先,一个好的监控系统应该非常迅速地接收,处理和记录来自外部的数据。 该帐户为微秒。 暂且看来,这似乎并不明显,但是当系统变得足够大时,所有这些秒的分数都会加起来,从而明显地引起明显的延迟。

第二项任务是提供对大量先前收集的指标(换句话说,对历史数据)的便捷访问。 历史数据用于各种各样的情况。 例如,从中生成报告和图形,在其上构建汇总检查,触发器取决于它们。 如果访问历史记录有任何延迟,那么这将立即影响整个系统的速度。
第三,历史数据占用了大量空间。 即使是相对适度的监视配置也可以很快获得可靠的历史记录。 但是几乎没有人希望保留处理器的使用历史为5年,因此监视系统不仅应该能够很好地记录,而且还可以很好地删除历史记录(在Zabbix中,此过程称为“整理”)。 删除旧数据并不一定要像收集和分析新数据那样高效,但是繁重的删除操作会占用宝贵的DBMS资源,并且可能减慢更多关键操作的速度。
前两个问题通过缓存解决。 Zabbix支持多个专用缓存,以加快数据读取和写入操作。 DBMS机制本身不适合此处,因为 即使是最先进的通用缓存算法,也不会知道在给定时间点需要立即访问哪些数据结构。
监控和时间序列数据
只要数据在Zabbix服务器的内存中,一切都很好。 但是内存不是无限的,因此有时需要将数据写入(或读取)数据库。 而且,如果数据库性能严重落后于收集指标的速度,那么即使是最先进的特殊缓存算法也将在很长一段时间内无济于事。
第三个问题也归结为数据库性能。 要解决此问题,您需要选择一种可靠的删除策略,该策略不应干扰其他数据库操作。 默认情况下,Zabbix每小时删除几千条记录的历史数据。 如果数据收集的速度和数据库中的位置允许的话,您可以配置更长的整理时间或更大的数据包大小。 但是,由于有大量指标和/或收集指标的频率很高,因此正确的内务管理设置可能是一项艰巨的任务,因为数据删除时间表可能无法跟上记录新指标的步伐。
总之,监视系统从三个方向解决了性能问题:使用SQL INSERT查询收集新数据并将其写入数据库,使用SELECT查询访问数据,以及使用DELETE删除数据。 让我们看看如何执行典型的SQL查询:
- DBMS分析查询并检查其语法错误。 如果该请求在语法上正确,则引擎将构建语法树以进行进一步处理。
- 查询计划程序分析语法树,并计算执行请求的各种方式(路径)。
- 调度程序计算最便宜的方法。 在此过程中,它考虑了很多因素-表有多大,是否需要对结果进行排序,是否有适用于查询的索引等。
- 找到最佳路径后,引擎将通过访问所需的数据块(使用索引或顺序扫描)执行查询,应用排序和过滤条件,收集结果并将其返回给客户端。
- 对于插入,修改和删除查询,引擎还必须更新相应表的索引。 对于大型表,此操作可能比处理数据本身花费的时间更长。
- DBMS最有可能还会为查询调度程序的后续调用更新数据使用情况的内部统计信息。
通常,有很多工作要做。 大多数DBMS提供了大量用于查询优化的设置,但它们通常专注于一些普通的工作流,在这些工作流中,插入和删除记录的频率与更改频率大致相同。
但是,如上所述,对于监视系统,最典型的操作是以批处理方式添加和定期删除。 几乎不会发生更改以前添加的数据的情况,并且访问数据涉及使用聚合函数。 另外,通常,添加指标的值按时间排序。 此类数据通常称为
时间序列 :
时间序列是按临时顺序索引(或列出或涂鸦)的一系列数据点。
从数据库的角度来看,时间序列具有以下属性:
- 时间序列可以按时间顺序排列在磁盘上。
- 可以使用时间列为时间序列表建立索引。
- 大多数SQL SELECT查询将在时间指示列上使用WHERE,GROUP BY或ORDER BY子句。
- 通常,时间序列数据具有“到期日期”,之后可以将其删除。
显然,传统的SQL数据库不适合存储此类数据,因为通用优化未考虑这些质量。 因此,近年来,出现了许多新的,面向时间的DBMS,例如InfluxDB。 但是所有流行的时间序列DBMS都有一个重大缺点-缺乏完整的SQL支持。 而且,它们大多数甚至都不是CRUD(创建,读取,更新,删除)。
Zabbix可以以任何方式使用这些DBMS吗? 一种可能的方法是将历史数据进行存储,以将其存储到时间序列专用的外部数据库中。 鉴于Zabbix架构支持用于存储历史数据的外部后端(例如,在Zabbix中实现了Elasticsearch支持),乍一看,此选项看起来非常合理。 但是,如果我们为一个时间序列支持一个或多个DBMS作为外部服务器,那么用户将不得不考虑以下几点:
- 需要探索,配置和维护的另一个系统。 另一个跟踪设置,磁盘空间,存储策略,性能等的地方。
- 降低监控系统的容错能力,例如 新链接出现在相关组件链中。
对于某些用户而言,专用于历史数据的专用存储的好处可能会超过不必担心另一个系统所带来的不便。 但是对于许多人来说,这是不必要的复杂性。 还值得记住的是,由于这些专用解决方案中的大多数都有自己的API,用于Zabbix数据库的通用层的复杂性将显着增加。 而且,理想情况下,我们更喜欢创建新功能,而不是与其他API对抗。
问题出现了-有没有办法利用DBMS的时间序列,而又不失去SQL的灵活性和优势? 自然,不存在通用答案,但是一个特定的解决方案非常接近答案
-TimescaleDB 。
什么是TimescaleDB?
TimescaleDB(TSDB)是PostgreSQL扩展,可优化常规PostgreSQL(PG)数据库中的时间序列工作。 尽管如上所述,市场上并不缺少可扩展的时间序列解决方案,但TimescaleDB的独特功能是它可以与时间序列很好地协同工作,而又不牺牲传统CRUD关系数据库的兼容性和优势。 实际上,这意味着我们可以两全其美。 数据库知道哪些表应视为时间序列(并应用所有必要的优化),但是您可以使用与常规表相同的方式来使用它们。 而且,不需要应用程序知道数据是由TSDB控制的!
要将表标记为时间序列表(在TSDB中,此表称为超表),只需调用create_ hypertable()TSDB过程。 在后台,TSDB根据指定条件将此表分为所谓的碎片(英文术语是“ chunk”)。 片段可以表示为表的自动控制部分。 每个片段都有相应的时间范围。 TSDB还为每个片段设置了特殊索引,以便使用一个数据范围不会影响对其他数据片段的访问。
来自timescaledb.com的超表图像当应用程序为时间序列添加新值时,扩展程序将此值定向到所需的片段。 如果未定义新值的时间范围,则TSDB将创建一个新的片段,为其分配所需的范围,然后在其中插入值。 如果应用程序从超级表请求数据,那么在执行请求之前,扩展将检查哪些片段与此请求相关联。
但这还不是全部。 TSDB对性能和可伸缩性进行了许多更改,从而完善了经过时间验证的PostgreSQL生态系统。 其中包括快速添加新记录,快速查询时间以及几乎免费的批量删除。
如前所述,为了控制数据库的大小并遵守保留策略(即,存储数据的时间不要超过必要的时间),好的监视解决方案应该有效地删除大量的历史数据。 使用TSDB,我们只需删除超表中的某些片段即可删除所需的故事。 在这种情况下,应用程序不需要按名称或任何其他链接来跟踪片段,TSDB将根据指定的时间条件删除所有必需的片段。
TimescaleDB和PostgreSQL分区
乍一看,TSDB似乎是对PG表的标准分区(
声明性分区 ,在PG10中正式称为)的很好的包装。 确实,要存储历史数据,可以使用标准分区PG10。 但是,如果仔细观察,TSDB和PG10部分的片段远非相同的概念。
首先,在PG中设置分区需要更深入地了解细节,应用程序本身或DBMS应该很好地做到这一点。 首先,您需要计划您的部分层次结构并决定是否使用嵌套分区。 其次,您需要提出一个节命名方案,并以某种方式将其转移到用于创建方案的脚本中。 最有可能的是,命名方案将包括日期和/或时间,并且这样的名称将需要以某种方式自动实现。
接下来,您需要考虑如何删除过期的数据。 在TSDB中,您可以简单地调用drop_chunks()命令,该命令确定在给定时间段内要删除的片段。 在PG10中,如果需要从标准PG部分中删除某个范围的值,则必须自己计算该范围的部分名称列表。 如果所选分区方案涉及嵌套部分,则这会使删除操作更加复杂。
需要解决的另一个问题是如何处理超出当前时间范围的数据。 例如,数据可能来自将来尚未创建节的数据。 或从过去查看已删除的部分。 在PG10中,默认情况下,添加这样的记录将不起作用,我们只会丢失数据。 在PG11中,您可以为此类数据定义一个默认部分,但这只能暂时掩盖问题,而不能解决问题。
当然,上述所有问题都可以以一种或另一种方式解决。 您可以将触发器,cron-jabs挂在基座上,并随意添加脚本。 这将是丑陋的,但可以正常工作。 毫无疑问,PG节比巨型整体表更好,但是,通过脚本和触发器绝对不能解决的问题是PG没有的时序改进。
即 与PG部分相比,TSDB Hypertables不仅可以节省数据库管理员的精力,而且还可以通过优化对数据的访问和添加新数据来区分它们。 例如,TSDB中的片段始终是一维数组。 这简化了片段管理并加快了插入和选择的速度。 为了添加新数据,TSDB在所需的片段中使用其自己的路由算法,与标准PG不同,该算法不会立即打开所有部分。 对于大量的部分,性能上的差异可能会显着变化。 可以在
本文中找到有关PG和TSDB中标准分区之间差异的技术详细信息。
Zabbix和TimescaleDB
在所有选项中,TimescaleDB似乎是Zabbix及其用户的最安全选择:
- TSDB被设计为PostgreSQL扩展,而不是独立系统。 因此,它不需要其他硬件,虚拟机或基础架构中的任何其他更改。 用户可以继续使用他们选择的PostgreSQL工具。
- TSDB允许您几乎不变地保存用于在Zabbix中使用数据库的所有代码。
- TSDB大大提高了历史记录同步器和管家的性能。
- 入门门槛低-TSDB的基本概念非常简单明了。
- 扩展本身和Zabbix的轻松安装和配置将极大地帮助中小型系统的用户。
让我们看看使用全新安装的Zabbix启动TSDB需要做什么。 安装Zabbix并运行PostgreSQL数据库创建脚本后,您需要在所需平台上下载并安装TSDB。 请参阅
此处的安装说明。 安装扩展程序后,需要为Zabbix基础启用它,然后运行Zabbix随附的timecaledb.sql脚本。 如果安装来自源,则位于数据库/postgresql/timecaledb.sql中;如果安装来自软件包,则位于/usr/share/zabbix/database/timecaledb.sql.gz中。 仅此而已! 现在,您可以启动Zabbix服务器,它将与TSDB一起使用。
timescaledb.sql脚本很简单。 他所做的只是将常规Zabbix历史记录表转换为TSDB超表并更改默认设置-设置参数“覆盖项目历史记录周期”和“覆盖项目趋势周期”。 现在(4.2版),以下Zabbix表在TSDB的控制下工作-历史记录,history_uint,history_str,history_log,history_text,趋势和趋势_uint。 可以使用相同的脚本来迁移这些表(请注意,migrate_data参数设置为true)。 必须记住,数据迁移是一个非常长的过程,可能需要几个小时。
chunk_time_interval => 86400参数在运行timecaledb.sql之前也可能需要更改,Chunk_time_interval是限制值落入该片段的时间的间隔。 例如,如果您将chunk_time_interval间隔设置为3小时,则整天的数据将分布在8个片段中,第一个片段1覆盖前3小时(0:00-2:59),第二个片段2覆盖-第二个3小时( 3:00-5:59),等等。 最后一个片段8将包含时间为21:00-23:59的值。 平均默认值是86400秒(1天),但是已加载系统的用户可能希望降低它。
为了粗略估计内存需求,重要的是要了解一块内存平均可以占用多少空间。 一般原则是,系统必须有足够的内存来排列每个超表的至少一个片段。 当然,在这种情况下,片段大小的总和不仅应有一定的余量,而且应小于postgresql.conf中shared_buffers参数的值。 可在TimescaleDB文档中找到有关此主题的更多信息。
例如,如果您有一个主要收集整数指标的系统,并且决定将history_uint表拆分为2小时的片段,然后将其余表拆分为一日的片段,则需要在timecaledb.sql中更改此行:
SELECT create_hypertable('history_uint', 'clock', chunk_time_interval => 7200, migrate_data => true);
在积累了一定数量的历史数据之后,可以通过调用chunk_relation_size()检查history_uint表的片段大小:
zabbix=> SELECT chunk_table,total_bytes FROM chunk_relation_size('history_uint'); chunk_table | total_bytes -----------------------------------------+------------- _timescaledb_internal._hyper_2_6_chunk | 13287424 _timescaledb_internal._hyper_2_7_chunk | 13172736 _timescaledb_internal._hyper_2_8_chunk | 13344768 _timescaledb_internal._hyper_2_9_chunk | 13434880 _timescaledb_internal._hyper_2_10_chunk | 13230080 _timescaledb_internal._hyper_2_11_chunk | 13189120
可以重复此调用以查找所有超表的片段大小。 例如,如果发现history_uint的片段大小为13MB,其他历史记录表的片段(例如20MB)和趋势表的片段为10MB,则总内存需求为13 + 4 x 20 + 2 x 10 = 113MB。 我们还必须从shared_buffers保留空间来存储其他数据,例如20%。 然后,shared_buffers的值必须设置为113MB / 0.8 =〜140MB。
为了更好地调整TSDB,最近出现了timescaledb-tune实用程序。 它分析postgresql.conf,将其与系统配置(内存和处理器)相关联,然后就设置内存参数,并行处理参数WAL提供建议。 该实用程序更改了postgresql.conf文件,但是您可以使用-dry-run参数运行它并检查建议的更改。
我们将详细介绍Zabbix参数的“替代项目历史记录周期”和“替代项目趋势周期”(可在“管理->常规->客房整理”中找到)。 需要使用它们来删除历史数据(作为TSDB超表的整个片段,而不是记录)。
事实是,Zabbix允许您分别设置每个数据元素(度量标准)的内务处理期。 但是,这种灵活性是通过扫描元素列表并在每次整理工作中计算各个周期来实现的。 如果系统对各个元素有各自的内务处理周期,那么显然系统无法为所有指标提供单个截止点,Zabbix将无法给出正确的命令来删除必要的片段。 因此,通过关闭指标的替代历史记录,Zabbix将失去通过调用history_ *表的drop_chunks()过程快速删除历史记录的能力,因此,关闭替代趋势将失去trends_ *表的相同功能。
换句话说,要充分利用新的内务管理系统,您需要将这两个选项都设为全局。 在这种情况下,内务处理将完全不读取数据项设置。
TimescaleDB的性能
现在是时候检查以上所有条件是否真的有效了。 我们的测试平台是带有PostgreSQL 10.7的Zabbix 4.2rc1和用于Debian 9的TimescaleDB 1.2.1。测试计算机是10核Intel Xeon,具有16 GB RAM和60 GB SSD上的存储空间。 按照今天的标准,这是一个非常适度的配置,但是我们的目标是找出TSDB在现实生活中的有效性。 在预算不受限制的配置中,您只需插入128-256 GB的RAM,然后将大部分(如果不是全部)数据库放入内存。
我们的测试配置包含32个活动的Zabbix代理,这些代理将数据直接传输到Zabbix服务器。 每个代理服务10,000个项目。 Zabbix历史缓存设置为256MB,shared_buffers PG设置为2GB。 此配置在数据库上提供了足够的负载,但是同时不会在Zabbix服务器进程上创建很大的负载。 为了减少数据源和数据库之间的活动部件数量,我们没有使用Zabbix代理。
这是从标准PG系统获得的第一个结果:

TSDB的结果完全不同:

下图将两个结果结合在一起。 工作始于170-200K的较高NVPS值,因为 开始与数据库同步之前,需要花费一些时间来填充历史记录缓存。

当历史表为空时,TSDB中的写入速度可与PG中的写入速度相媲美,即使后者的余量很小。 历史记录的数量一旦达到50-60百万,PG的吞吐量就会下降到110K NVPS,但是,更令人不愉快的是,它会与历史表中累积的记录数量成反比地变化。 同时,TSDB在整个测试过程中保持了130K NVPS的稳定速度,从0到3亿条记录。
总的来说,在我们的示例中,平均性能的差异非常明显(130K与90K(不考虑初始峰值)。 还可以看出,标准PG的插入率在很宽的范围内变化。 因此,如果工作流需要在历史记录中存储数千万或几亿条记录,但是没有用于非常积极的缓存策略的资源,那么TSDB是替代标准PG的强大候选者。
对于这种相对适度的系统,TSDB的优势已经很明显,但是很可能在大量的历史数据中差异将变得更加明显。 另一方面,该测试绝不是使用Zabbix的所有可能方案的概括。 自然,有许多因素会影响结果,例如硬件配置,操作系统设置,Zabbix服务器设置以及后台运行的其他服务的额外负载。 也就是说,您的里程可能会有所不同。
结论
TimescaleDB是一项非常有前途的技术。 它已经在严重的生产环境中成功运行。 TSDB与Zabbix配合良好,并且比标准PostgreSQL数据库具有明显的优势。
TSDB是否有任何缺陷或原因可以推迟使用? 从技术角度来看,我们看不出有任何反对意见。 但是应该记住,该技术仍然是新技术,其发布周期不稳定且功能开发的策略不清楚。 特别是,每两个月就会发布具有重大更改的新版本。 某些功能可能会被删除,例如自适应分块中发生的功能。 另外,作为不确定性的另一个因素,值得一提的是许可政策。 由于存在三个许可级别,因此非常令人困惑。 TSDB内核是在Apache许可下制造的,某些功能是在其自己的Timescale许可下发布的,但也有Enterprise的封闭版本。
如果您将Zabbix与PostgreSQL一起使用,则没有理由至少不尝试使用TimescaleDB。 也许这件事会让您感到惊喜:)请记住,在Zabbix中对TimescaleDB的支持仍处于试验阶段-在一段时间内,尽管我们收集了用户评论并获得了经验。