优化InfluxDB性能以处理高速数据
作者:Sam Dillard / 产品,用例,开发者
2019年10月24日
导航到
无论是以纳秒级分辨率追踪大脑中的神经元行为,监测支持业务关键应用的大型且不断变化的基础设施,追踪自动驾驶车辆的实时行为,还是监控股票及其交易所的股票价格的高频变化,对高分辨率数据的需求变得至关重要。为了满足这一需求,需要一个高速数据存储和查询引擎。
InfluxDB旨在同时满足这些需求。本文的目标是帮助您了解如何最大限度地利用它。
以下是一些前置说明
- 从本文中受益的先决条件是对InfluxDB数据模型(数据库、保留策略、度量、标签、字段)有基本的了解——参考、教程和以下基础。
- 为了结构化,以下展示的优化将按照典型时序数据管道的顺序编写。也就是说,最早的优化将一直从源头(数据源/客户端)开始,最后的优化将关于写入数据后如何使用数据(查询)。
- 最后,对于本文的范围,我假设使用TSI索引。
优化写入
首先,您需要确定如何监控您所监控的资产。InfluxData是开源收集代理Telegraf的创建者和维护者。然而,许多用户仍在使用其他监控方法,如客户端库。
如果可能,请使用Telegraf。Telegraf不仅提供了超过200个服务的插件驱动集成,而且是由与InfluxDB相同的工程团队设计的,使其成为InfluxDB最佳实践的框架。
Telegraf为您处理以下事项
- 排序标签和时间戳 - 为了更快地扫描文件块,InfluxDB按照字典顺序对键进行排序,然后按降序对时间戳进行排序。出于让InfluxDB不必担心这一点的目的,请在源头按字典顺序排序标签,并以升序顺序(最早先)写入您的批次。
- 重试 - 如果写入目标(InfluxDB、负载均衡器、消息队列等)出现故障或中断,客户端(在这种情况下,Telegraf)可能无法写入。假设您需要每条写入的数据完整性,那么您的客户端应该能够重试失败的写入,直到它们成功。
- 可修改的批次大小
- 抖动
- 有助于分散工作负载,因此可以提高数据节点的资源利用率。
- 如果您有多个写入器直接写入InfluxDB,这可以降低您遇到打开连接限制的可能性。
- 预处理
- 聚合(平均值、最小值、最大值、计数等)
- 转换 – 即字段到标签;标签到字段(在您需要向前移动时更改此类模式时很重要(见模式部分))。
数据模型、模式和行协议
鉴于本文的结构,您可能认为模式部分应该放在第一位。然而,InfluxDB基于接收到的写入具有自定义模式。您无需事先定义模式。这使得它非常灵活,但也可能导致“游荡”用户或标签在未来造成性能问题。InfluxDB组织数据的方式由行协议中的记录(或“行”)的写入方式定义。
关于行协议的简要介绍
数据模型
- 数据库 – 顶级数据结构;在单个Influx实例中可以有多个
- 保留策略 – 该策略中的数据在被删除之前将保持多长时间。
- 度量 – 相似指标的逻辑分组。例如:CPU使用情况可以通过许多方式来衡量。用于测量CPU写入度量(如
usage_user
、usage_system
、usage_idle
等)的Telegraf插件将它们全部写入一个名为cpu
的度量。度量可以被视为与关系数据库中的表功能相似。 - 标签集(元数据)
- 一个或多个键值对的组合,用作您正在写入的度量以及关联字段(实际指标)的元数据。
- 标签键用于描述正在监控的“资产”,例如:主机名、IP、地区、股票代码、交易所。
- 标签用于
GROUP
;您想要在单个查询中区分资产的数据。
- 字段集(指标)
- 一个或多个键值对的组合,用于描述资产的实际情况,例如:
cpu_usage_user
、memory_free
、disk_available
、water_pressure
、turbine_rpm
、open_price
等。
- 一个或多个键值对的组合,用于描述资产的实际情况,例如:
格式化行协议
在许多数据格式中 – 包括其他TSDB的数据格式 – 数据记录的结构是这样的,即只有其描述符元数据(定义“资产”的键)和其值(或标量)一起写入。这是必要的,因为度量键信息被编码在元数据中。行协议避免了这个问题,并允许您在每个记录中写入多个(许多)值 – 在这种情况下,每个“行”可以写入多个值。让我们看看一些比较
Graphite格式
host.metadata1.metadata2.<measureable>.<specific field> value=<value>
或者,更具体地说
度量1:0001.us-west-1.a.cpu.usage_system value=45.0
度量2:0001.us-west-2.a.cpu.usage_user value=35.0
在上面的例子中,value
之前的所有内容都是描述value
来源及其实际内容的键。这意味着,为了知道每个值代表什么,您必须将大部分元数据再写一次,仅仅是为了写另一个关于同一资产的CPU度量。
Prometheus格式
measureable_metric,metadata1,metadata2, gauge=<value>
或者,更具体地说
cpu_usage_system,region=us-west-1,host=0001,az=a gauge=1.5
cpu_usage_user,region=us-west-1,host=0001,az=a gauge=4.5
虽然这种格式将元数据的各个部分分开(有助于InfluxDB – 后文将讨论),但它与Graphite格式存在相同的问题。
Influx行协议格式
measureable,metadata1,metadata2 <specific_field>=<value>
Influx术语中
measurement,tag,tag field,field,field,field,field
更具体地说
cpu,region=us-west-1,host=0001,az=a usage_user=35.0,usage_system=45.0,usage_guest=0.0,usage_guest_nice=0.0,usage_nice=10.0,usage_steal=5.0
注意标点符号;标签和字段之间用空格分隔。
这种格式的几个优点
- 通过网络的负载变得更小。这有助于效率...和预算。
- 数据更易于探索,如下所示。 注意:拥有多个数据库、度量、标签和字段实例可以使在Chronograf数据探索器中查看数据更容易,并且通常使运行此探索的元查询更高效。
- 对磁盘的写入略快。
您如何将数据转换为这种格式?
优化读取
重要的是要了解InfluxDB的底层文件格式(InfluxData创建的时间结构合并树,与您可能更熟悉的行格式文件(例如,Postgres)不同。
- 缓存重复的响应数据
- 时间序列数据库不仅优化了时间序列写入,还优化了时间序列查询。时间范围限制、选择特定字段,如果可能的话,通过系列进行WHERE过滤
- 列式存储期望遇到列式风格的查询;对特定列进行
SELECT
(或过滤)的查询。这与常见的[SQL](https://influxdb.org.cn/glossary/sql/)查询SELECT * FROM
形成对比。 - 过滤
- 按时间过滤是查询的最明显形式,但许多刚接触时间序列数据库的新用户往往会忽略它。正如我之前所提到的,TSDB所做的两个设计假设是a)数据高速流动,b)数据越旧,其重要性越低。鉴于这一点,我们假设查询将处理时间片段,通常这些片段更接近当前。
- 按指标/资产/系列过滤同样重要。时间序列数据库(列式存储)不优化像关系型数据库可能做的那样从每个单独的“列”中读取数据。这意味着如果可能的话,只选择您需要数据的字段,并使用
WHERE
过滤您的标签,以便查询引擎只扫描包含您所需信息的系列键。
- 列式存储期望遇到列式风格的查询;对特定列进行
- 聚合比返回原始、未处理数据的查询计算得更快。
- 批处理函数需要在处理之前将所有数据读入内存,而流式函数则不需要
- 批处理:
percentile()
、holt_winters()
、median()
、mode()
、spread()
、stddev()
- 流:
mean()
、min()
、max()
、first()
、last()
、top()
、sum()
、elapsed()
、moving_average()
- 批处理:
- 增加分片持续时间(包含写入性能和存储/压缩权衡)
- 当查询在存储中搜索以检索数据时,它必须为每个分片分配新的内存。许多分片意味着更多的内存使用。
- 创建一个游标来引用每个分片中的每个系列。
- 如果您运行一个查询,评估3个分片中的1,000个系列的单个字段,至少会生成3,000个游标...这具有CPU和内存影响。
- 大部分情况下,“高查询负载”由访问的分片数量定义,无论是单个查询访问多个还是多个查询各自访问一个唯一的分片。
- 当查询在存储中搜索以检索数据时,它必须为每个分片分配新的内存。许多分片意味着更多的内存使用。
- 如果您使用Grafana或Chronograf等UI工具,可以利用仪表板模板变量。当适用时,这是一个很好的工具,可以用于过滤和防止数据过载抓取。
分片持续时间
- 更长
- 由于活跃/"热"分片(分片持续时间越长,分片就越“热”或未压缩,查询越优化),整体读写性能更好
- 更高效的压缩
- 更少的压缩
- 更短
- 更便宜的压缩
- 更易于管理
EXPLAIN ANALYZE
这是一个您可以用来分析您认为性能不佳的查询的工具。
运行它
EXPLAIN ANALYZE <query>
其输出如下所述
游标
- 遍历单个系列分片的指针
- 每个分片和每个系列都会创建一个游标(一个查询在3个分片中的1,000个系列中搜索1个字段将产生至少3,000个游标)
迭代器
- 存储和查询之间的主要接口
- 迭代器节点(以及嵌套迭代器)是查询引擎的“工作马”,执行排序、合并、聚合/缩减、表达式评估等操作。
执行时间
- 游标读取数据的时间
- 查询在流经每个迭代器并最终由查询执行器处理时执行转换的时间
- 查询执行器将结果序列化为JSON、CSV或其他指定的格式。
- 分析时,输出数据将被丢弃。
计划时间
- 在“选择节点”下,表示总查询计划时间
- 在“创建迭代器”下,表示特定迭代器的计划时间
规划步骤
- 确定查询类型(原始或带表达式的查询)
- 确定并分离时间范围和过滤条件表达式
- 确定查询的有效时间范围并根据该时间范围和索引列表中的测量值选择适当的分片(可能包括集群中的远程节点)
- 对于每个分片和其中的每个测量值
- 从索引中选择所有匹配的系列键,并按WHERE子句中的任何标签谓词进行过滤
- 根据GROUP BY维度将过滤后的系列整理到标签集合中
- 枚举每个集合并创建:每个系列创建一个游标和一个迭代器来读取TSM数据,并为每个组创建一个迭代器集合来执行所需的转换
- 合并所有迭代器并将合并后的结果返回给查询执行器
系列键的数量决定了查询可以多快地规划以及需要多少内存。
本指南之外,关于性能优化的具体做法,您可以根据自己的使用情况随时联系我们。如果您是企业客户,我们的支持团队、销售工程团队和客户成功团队将为您提供帮助。对于社区用户,我们有一个社区网站和一个社区Slack。
任何设计为灵活性的平台都意味着有很多方法可以提高对该平台的使用。虽然本指南中提供的调整旋钮众多,但并非所有都与每个用户都相关。希望每位读者都能学到至少几件可以提高整体性能的事情。