解决使用 InfluxDB 时失控的序列基数
作者:Rick Spencer / 产品, 用例, 开发者
2020年10月01日
导航至
在本文中,您将了解导致 高序列基数 在 时序数据库 中的原因,以及如何定位和消除罪魁祸首。首先,对于那些刚接触这个概念的人,让我们定义一下它:InfluxDB 实例中唯一数据库、测量、标签集和字段键组合的数量。由于高序列基数是许多数据库工作负载高内存使用率的主要驱动因素,因此了解其成因以及如何解决它非常重要。
失控基数的症状
人们通常通过以下两种方式之一发现他们遇到了基数问题
- 他们达到了 InfluxDB Cloud 上的基数限制。
- 他们注意到在 InfluxDB Cloud 或 InfluxDB OSS 2.0 上,读取速度,有时写入速度,变得越来越慢。
当您写入 InfluxDB 时,InfluxDB 使用您使用的测量和标签创建索引,以加快读取速度。但是,当创建的索引过多时,写入和读取实际上都会开始减慢。
查找失控基数的来源
索引是为标签值的每个唯一组合和所有字段名称创建的。这在 高基数的危险信号 和 InfluxDB 的数据布局和模式设计最佳实践 中有更详细的描述。基数或多或少通过如下公式计算
(count(标签 1 值) * count(标签 2 值) … count(标签 n 值)) * count(字段名称)
请注意,在某些情况下,由于存在相关标签,此公式可能会高估序列基数,如此处所述。
寻找失控序列基数的唾手可得的成果
通常,是单个标签导致了失控的基数。通常,这是一个标签,最终几乎每个条目都有一个唯一的值。
一些常见的罪魁祸首是
- 使用日志消息作为标签值,但结果表明日志消息具有时间戳、指针值或其他唯一字符串。
- 使用时间戳作为标签。这通常是在客户端代码中意外完成的。
- 使用一些唯一的东西,但只是有非常多的值。例如,如果您用用户 ID 标记每个条目,这很好,除非您有成千上万或数百万的用户。
使用 InfluxDB 深入挖掘可疑标签
如果没有立即怀疑的标签,那么您只需找到基数方程的值,以便追踪失控基数的来源。如果您不确定哪个存储桶导致了失控的基数,您可能需要对多个存储桶执行此操作。
查找字段名称很容易。您可以使用以下查询
import "influxdata/influxdb/v1"
v1.fieldKeys(bucket: "idping", start: -100y)
|> count()
然而,通常情况下,标签值是罪魁祸首。以下是一个查询,它将计算存储桶中每个标签的值的数量
import "influxdata/influxdb/v1"
cardinalityByTag = (bucket) =>
v1.tagKeys(bucket: bucket)
|> map(fn: (r) => ({
tag: r._value,
_value: if contains(set: ["_stop","_start"], value:r._value) then
0
else
(v1.tagValues(bucket: bucket, tag: r._value)
|> count()
|> findRecord(fn: (key) => true, idx: 0))._value
}))
|> group(columns:["tag"])
|> sum()
cardinalityByTag(bucket: "my-bucket")
上述查询将显示哪些标签对基数的贡献最大,并且很可能有一个标签比其他标签高出几个数量级。但是,如果您遇到失控的基数,此查询可能会超时,无法完成计算。如果您遇到超时,您可以一次完成以下步骤。
首先,您可以使用以下查询生成标签列表
import "influxdata/influxdb/v1"
v1.tagKeys(bucket: "my-bucket")
|> yield(name: "tags")
然后,对于每个标签,使用如下查询查找与该标签关联的值的数量
import "influxdata/influxdb/v1"
v1.tagValues(bucket: "my-bucket", tag: "my-tag")
|> count()
这些查询应该为您提供识别每个存储桶中基数来源所需的数字。要确定哪些特定标签正在增长,请在 24 小时后再次检查基数,看看是否有一个或多个标签显着增长。
修复您的模式
通常,解决失控基数就像将有问题的标签更改为字段一样简单。如果您担心这会如何影响查询性能,下面将讨论解决方案。
删除数据以减少高基数
有时您实际上并不需要导致高基数的数据。在这些情况下,您可以使用 delete API 端点删除一些数据,或者直接删除整个存储桶。请注意,您的帐户可能需要几个小时才能反映基数的减少。
设计模式或读取性能
通常,使用标签的价值在于创建索引,而创建索引的价值在于在查询期间,查询引擎不需要扫描数据库中的每个记录。但是,如上所述,拥有过多的索引会产生自身的性能问题。因此,诀窍是在扫描和索引之间创建中间地带。
例如,假设您想经常查询特定的用户 ID,但您有成千上万的用户。像这样的简单查询
from(bucket: "my-bucket")
|> range(start: -7d)
|> filter(fn: (r) => r.userId == "abcde")
其中 userId 是一个字段,InfluxDB 将导致扫描存储中 userId 的每一行。但是,如果您在 模式中包含可以合理索引的标签,则可以大大减少扫描的行数。例如,假设您有成千上万的用户,但每个用户都可以按类型、位置、公司等进行分类。您可以包含一个值少得多的标签,以便可以合理地索引它。因此,此查询
from(bucket: "my-bucket")
|> range(start: -7d)
|> filter(fn: (r) => r.companyTag == "Acme")
|> filter(fn: (r) => r.userId == "abcde")
可以执行得更快,因为“companyTag”已索引,因此可以快速检索该组行,然后 userId 的扫描可以扫描更少的行。
希望本文已帮助您解决了基数问题,并使您的 InfluxDB 项目重回正轨。一旦您了解了基数的贡献因素以及与查询的关系,您就可以设计系统以更快地工作。