InfluxDB数据布局和模式设计最佳实践
作者:Anais Dotis-Georgiou / 产品,用例,开发者
2020年8月28日
导航至
确定InfluxDB v2的最佳数据布局对于优化InfluxDB使用的资源、提高数据摄取率和查询及任务的性能至关重要。您还希望考虑开发者和用户体验(UX)。本文将向您介绍开发物联网应用程序示例的架构,并解答以下问题:
- InfluxDB v2的架构设计和数据布局的一般建议是什么?
- 内存、CPU和存储等计算资源会受到哪些影响?我如何优化InfluxDB以适应可用资源?
- 我如何降低资源使用?
- 我如何优化InfluxDB架构以提升查询和降采样性能?
- 在设计InfluxDB架构时,我还应考虑哪些其他因素?
- 我的组织将如何与数据交互?用户体验设计如何影响架构?
- 安全性和授权考虑如何影响架构设计?
- 常见的架构设计错误有哪些?
TL;DR 架构设计和数据布局的一般建议
一般来说,您应该遵守以下建议
- 将元数据编码到标签中。度量(Measurements)和标签(tags)是索引的,而字段值(field values)不是。通常查询的元数据应存储在标签中。标签值是字符串,而字段值可以是字符串、浮点数、整数或布尔值。
- 限制系列数量或尝试减少系列基数。
- 保持桶(bucket)和度量(measurement)名称简短且简单。
- 避免在度量名称中编码数据。
- 当需要为数据分配不同的保留策略或需要认证令牌时,将数据分开到不同的桶中。
最小化InfluxDB实例
本节介绍了影响运行InfluxDB实例所需资源的InfluxDB概念。我们将学习以下内容:
- 系列基数
- 如何计算系列基数
- 如何摄入率影响InfluxDB实例大小
- 降采样
系列基数
您应该旨在降低系列基数,以最大限度地减少您的InfluxDB实例大小和成本。系列基数是在一个桶、度量、标签集和字段键组合中组织中独特的数量。InfluxDB使用一个名为TSI的时间序列索引。TSI使InfluxDB能够处理极高的系列基数。TSI通过在内存中提取常用数据并移动不常访问的数据到磁盘上来支持高摄入率。设计您的数据模式以降低系列基数需要一些前瞻性和规划。
在上图中,蓝色、橙色和灰色线条代表不同的系列基数。例如,如果您在r4.4xl实例上运行InfluxDB,系列基数为10,000,您每秒可以摄入超过700,000个点。
计算基数
在我们思考如何优化我们的数据模式以降低系列基数之前。让我们计算一个植物监控应用MyPlantFriend的系列基数。MyPlantFriend监控用户的植物环境(存储温度、湿度、湿度和光照指标)。数据布局如下
桶 | MyPlantFriend |
度量 | MyPlantFriend |
标签键 |
|
字段键 |
|
假设我们有两个用户和几种类型的植物物种
用户 | 植物物种 |
cool_plant_lady | 兰花 |
cool_plant_lady | 常见多肉植物 |
优秀多肉植物 | 刺梨 |
优秀多肉植物 | 芦荟 |
优秀多肉植物 | 玉树 |
我们看到我们有2个标签键。对于用户标签键,我们有2个标签值。对于植物物种标签键,我们有5个标签值。
我们有1个桶,1个度量,和4个字段键。
我们的系列基数将是
您的首要任务是避免系列基数失控。系列基数失控发生在您加载标签或度量中的数据可能是不确定的。上面的示例是一个完美的示例,这种模式可能导致系列基数失控。假设您的用户会增长,将用户名包含在标签中是一种快速让你的基数爆炸的方法。相反,最好将用户名保留为字段。更多关于系列基数失控的示例在本文的最后部分。
摄入率如何影响InfluxDB
系列基数只能告诉你InfluxDB实例维度和性能故事的一半。最小化系列基数应该是您战略规划努力的一部分,以减少您的成本和InfluxDB大小,但您必须在系列摄入率中确定系列基数的上下文。系列摄入的频率在您的整体实例大小中起着重要作用。
例如,设想如果我在MyPlantFriend测量中添加了一些用户指标,比如设备ID和电子邮件地址。这些信息不应该在每次我们写入新的环境指标时都写入数据库。每次写入温度指标时都写入相同的电子邮件地址是多余的。事实上,我们可能只想在用户注册时写入这些数据一次。因此,将此用户数据写入不同的存储桶是有意义的。然而,这个例子仅限于MyPlantFriend物联网应用,我们假设字段之间互不相关。请阅读“InfluxDB模式设计的最终考虑”部分,了解用户体验如何影响模式设计。
将我们的数据集(用户数据和MyPlantFriend指标)写入不同的存储桶提供了以下优点
- 我们可以在注册时一次性写入数据,并减少摄入量
- 存储桶是InfluxDB模式组织层次结构中的顶层,并且与它们相关联一个保留策略——每个数据点持久存在的时长。将数据分离到不同的存储桶允许我们为我们的数据分配不同的保留策略。例如,用户数据存储桶将有一个无限保留策略。而MyPlantFriend存储桶将有一个较短的保留策略,因为传感器指标收集频率高,保留历史数据并不重要。
- 我们可以优化我们的常见查询和降采样 任务以提高性能。我们将在下一节中描述实例大小、模式设计以及查询性能之间的关系。
降采样
与我们将数据区分到不同的存储桶以应用不同的保留策略类似,我们也必须考虑我们想要如何降采样我们的数据。降采样是使用任务将您的数据从高精度形式聚合到低精度形式的过程。例如,MyPlantFriend的降采样任务可能如下所示
- 将每小时温度和光照指标降采样到每周的最小和最大值。
- 将每小时土壤湿度和湿度指标降采样到每日平均值。
结合保留策略,降采样可以减少InfluxDB的实例大小,因为针对降采样数据的查询扫描、处理和传输的数据更少。您应该在可以承担失去高精度数据或查询可以精确用降采样聚合来回答时执行降采样。使用保留策略删除旧的高精度数据。这将节省存储空间并提高查询性能。
优化模式以使用资源
了解何时应用降采样相对简单,然而优化InfluxDB模式以增加降采样任务性能并减少资源使用并不简单。更深入地考虑降采样引导我们走向新的模式设计。记住,存储桶、测量值和标签是索引的,而字段值不是。因此,按存储桶、测量值和标签过滤的查询和任务比按字段过滤的查询和任务更高效。由于我们预计将MyPlantFriend字段(air_temp、soil_moisture、humidity和light)以不同的速率降采样,将字段分配到不同的测量值中提供以下优点
- 查询更简单(这特别有用,如果您打算创建模板或在不同组织间重用查询)。
- 一个在单个测量值中查询4个字段的Flux查询示例
from(bucket: "MyPlantFriend")
|> range(start: v.timeRangeStart, stop: v.timeRangeStop)
|> filter(fn: (r) => r["_measurement"] == "MyPlantFriend")
|> filter(fn: (r) => r["_field"] == "light")
|> max()
- 将字段拆分为单独测量值后的Flux查询示例
from(bucket: "MyPlantFriend")
|> range(start: v.timeRangeStart, stop: v.timeRangeStop)
|> filter(fn: (r) => r["_measurement"] == "light")
|> max()
- 后者查询更简单,性能更高,并且减少了内存使用。
优化模式设计以提高查询和任务性能的重要性与您任务的数目和频率成正比。在确认您没有遇到系列基数问题后,您希望在最小化系列基数、通过降采样和保留策略减少实例大小以及优化模式设计以提升查询和任务性能之间取得平衡。
InfluxDB模式设计的最终考虑因素
思考系列基数、数据摄取速率和查询性能对实例大小的影响只是良好数据模式设计的一部分。您还应该考虑您的用户和开发者如何与您的时序数据进行交互,以优化您的InfluxDB模式以提升开发者体验。
MyPlantFriend字段之间实际上并没有太多关联。我不预见用户需要比较字段或在这些字段上进行数学运算。这支持将字段分离到测量中的观点。让我们通过对比MyPlantFriend的用户体验与一个健康应用的用户体验来强调用户体验如何影响模式设计。人类有一套标准的生物健康指标。对于健康应用,我会将血氧水平、心率、体温写入一个测量中,因为我预见需要比较这些指标来评估人类健康。虽然您可以将字段分离到不同的测量中,但您可能需要使用joins()来执行测量或桶之间的数学运算,这会更耗费计算资源。
相比之下,将MyPlantFriend应用的字段分离到不同的测量中是一个好策略,因为joins()可能不会被使用或很少使用。这个模式改变将使开发者能够轻松编写查询,并为用户提供他们感兴趣的数据。
此外,如果您正在创建一个应用程序并使用API,您想通过将每个用户相关的数据写入不同的桶来解决安全和授权问题。InfluxDB 身份验证令牌确保用户与数据之间的安全交互。令牌属于一个组织,并标识组织内的InfluxDB权限。您可以将令牌范围限定到单个桶。
最终模式建议
在考虑逃逸基数、降采样和保留策略以及查询性能优化后,我提出以下模式设计
桶(保留策略 = 2周) | 湿度指标 |
度量 | airt_temp |
度量 | 光照 |
字段 | 空气温度 |
字段 | 光照 |
桶(保留策略 = 2天) | 干燥指标 |
度量 | 湿度 |
度量 | 土壤湿度 |
字段 | 湿度 |
字段 | 土壤湿度 |
桶(保留策略 = 无限) | 用户数据 |
度量 | user_data |
标签 | 地区 |
字段 | 用户名 |
字段 | 植物物种 |
记住,最终的架构结果是基于对以下特定于MyPlantFriend物联网应用程序的假设的考虑
- 用户名和植物种类应转换为字段以避免逃逸系列基数。
- 干湿指标需要不同的降采样和保留策略,这使它们适合分别存储在单独的桶中。
- 字段之间没有关联,跨测量和/或桶执行数学运算将非常罕见。如果字段相关,它们应包含在同一个测量中。
- 为区域添加了一个标签,以提供一个良好标签的示例。可能的区域数量是有限的,所以这不会导致基数无限制增长。
导致基数无限制增长的常见模式设计错误
错误 1:将日志消息作为标签记录。解决方案 1:我们不建议将日志存储为标签,因为存在无界基数(例如,日志可能包含唯一的时间戳、UUID等)。您可以将日志属性存储为标签,只要基数不是无界的。例如,您可以从日志消息中提取日志级别(错误、信息、调试)或某些关键字段。将日志存储为字段是可以的,但与其他解决方案相比,搜索效率较低(本质上相当于表扫描)。
错误 2:测量值过多。这种情况通常发生在人们从-或认为InfluxDB是-键值存储迁移过来时。例如,如果您将系统统计信息写入InfluxDB实例,可能会倾向于写入如下所示的数据:Cpu.server-5.us-west.usage_user value=20.0
解决方案 2:相反,将此信息编码为标签,如下所示:cpu, host=server-5, region = us-west, usage_user=20.0
错误 3:将ID(如eventid、orderid或userid)作为标签。这也是一个可以导致无界基数(如果标签值未限定范围)的例子。解决方案 3:相反,将这些度量作为字段。
希望这篇教程能帮助您了解如何最佳设计您的InfluxDB模式。就像往常一样,如果您遇到困难,请在我们社区网站或Slack频道上分享。我们很乐意得到您的反馈并帮助您解决遇到的问题。