InfluxDB schema设计与数据布局
每个 InfluxDB 用例都是独特的,你的 schema 反映了这种独特性。一般来说,专为查询设计的 schema 会导致更简单和更高性能的查询。我们建议针对大多数用例遵循以下设计准则:
数据存储位置(标签或字段)
您的查询应该指导您在 tags 中存储哪些数据,以及在 fields 中存储哪些数据:
- 在标签中存储常见查询和分组(
group()或GROUP BY)的元数据。 - 如果每个数据点包含不同的值,则将数据存储在字段中。
- 将数字值存储为字段(tag values 仅支持字符串值)。
避免过多的系列
IndexDB 索引以下数据元素以加快读取速度:
标签值是被索引的,而字段值则不是。 这意味着通过标签进行查询的性能优于通过字段进行查询。 然而,当创建的索引过多时,写入和读取都可能开始变慢。
每个独特的索引数据元素集合形成一个 series key。 Tags 包含高度可变的信息,如唯一标识符、哈希和随机字符串,导致大量的 series,也称为高 series cardinality。 高系列基数是许多数据库工作负载高内存使用的主要驱动因素。 因此,为了减少内存消耗,请考虑将高基数值存储在字段值中,而不是在标签或字段键中。
如果对InfluxDB的读取和写入开始变慢,您可能有高系列基数(系列过多)。 请参阅 如何找到和减少高系列基数。
使用推荐的命名约定
在命名您的标签和字段键时,请使用以下约定:
避免在标签和字段键中使用保留关键字
不需要,但避免在你的标签键和字段键中使用保留关键字会简化查询的编写,因为你不必将键用双引号括起来。请参阅 InfluxQL 和 Flux keywords 以避免。
此外,如果标签键或字段键包含其他字符 [A-z,_],则必须在InfluxQL中用双引号括起来,或在Flux中使用 bracket notation。
避免标签和字段同名
避免为标签和字段键使用相同的名称。 这通常在查询数据时导致意外行为。
如果您不小心为一个标签和一个字段添加了相同的名称,请参见 常见问题解答 以获取有关如何可预测地查询数据以及如何解决问题的信息。
避免在测量和键中编码数据
将数据存储在 tag values 或 field values 中,而不是 tag keys、field keys 或 measurements 中。如果您设计您的架构以将数据存储在标签和值字段中,您的查询将更容易编写且更高效。
此外,通过在写入数据时不创建测量和键,您将保持低基数。 要了解高系列基数对性能的影响,请参见 如何寻找和减少高系列基数。
比较模式
比较以下以行协议表示的有效模式。
推荐: 以下架构将元数据存储在单独的 crop、 plot 和 region 标签中。 temp 字段包含可变的数字数据。
Good Measurements schema - Data encoded in tags (recommended)
-------------
weather_sensor,crop=blueberries,plot=1,region=north temp=50.1 1472515200000000000
weather_sensor,crop=blueberries,plot=2,region=midwest temp=49.8 1472515200000000000
不推荐: 以下模式存储多个属性 (crop, plot 和 region) 连接在一起 (blueberries.plot-1.north) ,类似于 Graphite 指标。
Bad Measurements schema - Data encoded in the measurement (not recommended)
-------------
blueberries.plot-1.north temp=50.1 1472515200000000000
blueberries.plot-2.midwest temp=49.8 1472515200000000000
不推荐:以下模式在字段键中存储多个属性(crop,plot 和 region)的连接形式(blueberries.plot-1.north)。
Bad Keys schema - Data encoded in field keys (not recommended)
-------------
weather_sensor blueberries.plot-1.north.temp=50.1 1472515200000000000
weather_sensor blueberries.plot-2.midwest.temp=49.8 1472515200000000000
比较查询
比较以下的 良好测量 和 恶劣测量 方案的查询。
该 Flux 查询计算 北部 区域蓝莓的平均 温度
易于查询: 良好的测量数据 可以通过 region 标签值轻松过滤,例如:
// Query *Good Measurements*, data stored in separate tags (recommended)
from(bucket: "<database>/<retention_policy>")
|> range(start:2016-08-30T00:00:00Z)
|> filter(fn: (r) => r._measurement == "weather_sensor" and r.region == "north" and r._field == "temp")
|> mean()
查询困难: 不良测量 需要正则表达式来提取 plot 和 region 从测量中——例如:
// Query *Bad Measurements*, data encoded in the measurement (not recommended)
from(bucket: "<database>/<retention_policy>")
|> range(start:2016-08-30T00:00:00Z)
|> filter(fn: (r) => r._measurement =~ /\.north$/ and r._field == "temp")
|> mean()
复杂的测量使得某些查询变得不可能。例如,计算两个图的平均温度在 Bad Measurements 模式下是不可能的。
查询模式的 InfluxQL 示例
# Query *Bad Measurements*, data encoded in the measurement (not recommended)
> SELECT mean("temp") FROM /\.north$/
# Query *Good Measurements*, data stored in separate tag values (recommended)
> SELECT mean("temp") FROM "weather_sensor" WHERE "region" = 'north'
避免在一个标签中放入多个信息
将一个包含多个部分的单一标签拆分为单独的标签,可以简化您的查询,并通过减少对正则表达式的需求来提高性能。
考虑以下通过行协议表示的模式。
示例行协议模式
Schema 1 - Multiple data encoded in a single tag
-------------
weather_sensor,crop=blueberries,location=plot-1.north temp=50.1 1472515200000000000
weather_sensor,crop=blueberries,location=plot-2.midwest temp=49.8 1472515200000000000
Schema 1 数据编码了多个独立的参数,plot 和 region 合并为一个长标签值 (plot-1.north)。将其与以下以行协议表示的模式进行比较。
Schema 2 - Data encoded in multiple tags
-------------
weather_sensor,crop=blueberries,plot=1,region=north temp=50.1 1472515200000000000
weather_sensor,crop=blueberries,plot=2,region=midwest temp=49.8 1472515200000000000
使用Flux或InfluxQL计算north区域蓝莓的平均temp。由于使用多个标签,建议使用Schema 2,这样您不需要正则表达式。
查询架构的Flux示例
// Schema 1 - Query for multiple data encoded in a single tag
from(bucket:"<database>/<retention_policy>")
|> range(start:2016-08-30T00:00:00Z)
|> filter(fn: (r) => r._measurement == "weather_sensor" and r.location =~ /\.north$/ and r._field == "temp")
|> mean()
// Schema 2 - Query for data encoded in multiple tags
from(bucket:"<database>/<retention_policy>")
|> range(start:2016-08-30T00:00:00Z)
|> filter(fn: (r) => r._measurement == "weather_sensor" and r.region == "north" and r._field == "temp")
|> mean()
查询模式的 InfluxQL 示例
# Schema 1 - Query for multiple data encoded in a single tag
> SELECT mean("temp") FROM "weather_sensor" WHERE location =~ /\.north$/
# Schema 2 - Query for data encoded in multiple tags
> SELECT mean("temp") FROM "weather_sensor" WHERE region = 'north'
分片组持续时间管理
分片组持续时间概述
InfluxDB 将数据存储在分片组中。 分片组按 保留策略 (RP) 组织,并存储在特定时间间隔内的时间戳数据,该时间间隔称为 分片持续时间。
如果未提供分片组持续时间,则分片组持续时间由创建RP时的duration决定。默认值为:
| RP 时长 | 分片组时长 |
|---|---|
| < 2 天 | 1 小时 |
| >= 2 天并且 <= 6 个月 | 1 天 |
| > 6 个月 | 7 天 |
分片组持续时间在每个RP中也是可配置的。 要配置分片组持续时间,请参见 保留策略管理。
分片组持续时间权衡
确定最佳分片组持续时间需要在以下两个方面找到平衡:
- 更好的整体性能与更长的块
- 通过较短的分片提供的灵活性
长片段组持续时间
更长的分片组持续时间使InfluxDB能够在同一逻辑位置存储更多数据。这减少了数据重复,提高了压缩效率,并在某些情况下提高了查询速度。
短共享组持续时间
较短的分片组持续时间使系统能够更有效地删除数据并记录增量备份。当InfluxDB强制执行保留策略时,它会删除整个分片组,而不是单个数据点,即使这些数据点比保留策略持续时间更旧。只有当分片组的持续时间结束时间older than RP持续时间时,分片组才会被删除。
例如,如果您的RP的持续时间为一天,InfluxDB每小时丢弃一小时的数据,并始终保持25个分片组。 每个小时一个,另外还有一个部分过期的分片组,只有当整个分片组的时间超过24小时时才会被删除。
注意: 一个需要考虑的特殊用例:按时间过滤模式数据(例如标签、系列、测量)。例如,如果您想在一个小时的时间间隔内过滤模式数据,您必须将分片组持续时间设置为
1h。有关更多信息,请参见 按时间过滤模式数据。
分片组持续时间建议
默认的分片组持续时间适用于大多数情况。然而,高吞吐量或长时间运行的实例将受益于使用更长的分片组持续时间。以下是一些关于更长分片组持续时间的建议:
| RP 时长 | 分片组时长 |
|---|---|
| <= 1 天 | 6 小时 |
| > 1天并且<= 7天 | 1天 |
| > 7 天且 <= 3 个月 | 7 天 |
| > 3个月 | 30天 |
| 无限的 | 52周或更长时间 |
注意: 请注意
INF(无限)不是一个 有效的分片组持续时间。
较长的分片持续时间可能适用于某些使用案例(例如,回填),然而对较大分片的更新和删除会导致更高的重新压缩成本。
在设置分片组持续时间之前需要考虑的其他因素:
用于回填的分片组持续时间
大范围历史数据的批量插入会同时触发大量分片的创建。 对数百或数千个分片的并发访问和写入开销可能会迅速导致性能下降和内存耗尽。
在写入历史数据时,设置更长的分片组持续时间,以便InfluxDB创建更少的分片。通常,30天的分片组持续时间适合回填,但请考虑这些分片在未来是否可能再受到回填的影响。更长的分片持续时间在第一次回填数据集时更高效,但会使后续的分片回填成本更高。如果您预计某个分片需要多次回填、更新或删除,那么略短的持续时间可能更可取,以减少分片重压缩的资源成本。