Influxdb数据写入的最佳实践

769 阅读9分钟

一、优化对influxdb的写入

1. 批量写入

批量写入,最佳的批量大小是5000行

2.按键值对标签进行排序

在将数据点写入 InfluxDB 之前,请按字典顺序对标签进行排序。 验证排序结果与Gobytes.Compare函数的结果匹配。

# Line protocol example with unsorted tags
measurement,tagC=therefore,tagE=am,tagA=i,tagD=i,tagB=think fieldKey=fieldValue 1562020262

# Optimized line protocol example with tags sorted by key
measurement,tagA=i,tagB=think,tagC=therefore,tagD=i,tagE=am fieldKey=fieldValue 1562020262

3.使用尽可能粗略的时间精度

默认情况下,InfluxDB 以纳秒精度写入数据。但是,如果您的数据不是在纳秒内收集的,则无需以该精度进行写入。为了获得更好的性能,请对时间戳使用尽可能粗的精度。

4.使用gzip压缩

使用 gzip 压缩来加快对 InfluxDB 的写入速度并减少网络带宽。基准测试表明,压缩数据后速度可提高 5 倍

  1. 在telegraf中使用gzip压缩
  2. 客户端库中使用gzip压缩
  3. influxdb api中使用gzip压缩
echo "airSensors,sensor_id=TLM0201 temperature=73.97038159354763,humidity=35.23103248356096,co=0.48445310567793615 1630525358 
  airSensors,sensor_id=TLM0202 temperature=75.30007505999716,humidity=35.651929918691714,co=0.5141876544505826 1630525358" | gzip > air-sensors.gzip

curl --request POST \
"http://localhost:8086/api/v2/write?org=YOUR_ORG&bucket=YOUR_BUCKET&precision=ns" \
  --header "Authorization: Token YOUR_API_TOKEN" \
  --header "Content-Encoding: gzip" \
  --header "Content-Type: text/plain; charset=utf-8" \
  --header "Accept: application/json" \
  --data-binary @air-sensors.gzip

5.将主机与ntp同步

使用网络时间协议 (NTP) 来同步主机之间的时间。如果线路协议中不包含时间戳,InfluxDB 将使用其主机的本地时间(UTC 格式)为每个点分配时间戳。如果主机的时钟未与 NTP 同步,时间戳可能不准确。

6.在一个请求中写入多个数据点

要在一个请求中写入多行,线路协议的每一行必须用新行 ( ) 分隔\n

7.速率限制

使用该标志来控制写入速率。使用以下字符串格式之一指定速率限制:influx write --rate-limit

  • COUNT(B|kB|MB), 或者
  • COUNT(B|kB|MB)/TIME(s|sec|m|min)

其中COUNT是十进制数,TIME是正整数。值中的空格将被忽略。例如:“5MB/5分钟”也可以表示为17476.266666667Bs、、、、或。如果速率限制格式无效,则打印出该格式和精确的正则表达式。该标志也可以与 一起使用。1MB/1min``1MB/min``1MBmin``1MBm``influx write``--rate-limitinflux write dryrun

二、处理重复的数据点

对于具有相同测量名称、标签集和时间戳的点,InfluxDB会创建旧字段的并集,对任何匹配的字段键,InfluxDB使用新的字段值

# 存在的数据点
web,host=host2,region=us_west firstByte=24.0,dnsLookup=7.0 1559260800000000000

# 新的数据点
web,host=host2,region=us_west firstByte=15.0 1559260800000000000

提交新数据点后,InfluxDB 会使用新字段值覆盖firstByte,并保留字段 dnsLookup:

# 合并结果
web,host=host2,region=us_west firstByte=15.0,dnsLookup=7.0 1559260800000000000
from(bucket: "example-bucket")
  |> range(start: 2019-05-31T00:00:00Z, stop: 2019-05-31T12:00:00Z)
  |> filter(fn: (r) => r._measurement == "web")

Table: keys: [_measurement, host, region]
               _time  _measurement   host   region  dnsLookup  firstByte
--------------------  ------------  -----  -------  ---------  ---------
2019-05-31T00:00:00Z           web  host2  us_west          7         15

保留重复点位

保留重复点 要保留重复点中的旧字段值和新字段值,请使用以下策略之一: 添加任意标签 增加时间戳。

# 存在的点位
web,host=host2,region=us_west,uniq=1 firstByte=24.0,dnsLookup=7.0 1559260800000000000

# 新点位
web,host=host2,region=us_west,uniq=2 firstByte=15.0 1559260800000000000

无需将唯一标签追溯添加到现有数据点。 标签集作为一个整体进行评估。 新点上的任意 uniq 标签允许 InfluxDB 将其识别为唯一点。 然而,这会导致两点的模式不同,并且可能会在查询数据时带来挑战。

from(bucket: "example-bucket")
  |> range(start: 2019-05-31T00:00:00Z, stop: 2019-05-31T12:00:00Z)
  |> filter(fn: (r) => r._measurement == "web")

Table: keys: [_measurement, host, region, uniq]
               _time  _measurement   host   region  uniq  firstByte  dnsLookup
--------------------  ------------  -----  -------  ----  ---------  ---------
2019-05-31T00:00:00Z           web  host2  us_west     1         24          7

Table: keys: [_measurement, host, region, uniq]
               _time  _measurement   host   region  uniq  firstByte
--------------------  ------------  -----  -------  ----  ---------
2019-05-31T00:00:00Z           web  host2  us_west     2         15

将时间戳增加一纳秒,保证点位的唯一性

# Old data point
web,host=host2,region=us_west firstByte=24.0,dnsLookup=7.0 1559260800000000000

# New data point
web,host=host2,region=us_west firstByte=15.0 1559260800000000001

将新点写入 InfluxDB 后:

from(bucket: "example-bucket")
  |> range(start: 2019-05-31T00:00:00Z, stop: 2019-05-31T12:00:00Z)
  |> filter(fn: (r) => r._measurement == "web")

Table: keys: [_measurement, host, region]
                         _time  _measurement   host   region  firstByte  dnsLookup
------------------------------  ------------  -----  -------  ---------  ---------
2019-05-31T00:00:00.000000000Z           web  host2  us_west         24          7
2019-05-31T00:00:00.000000001Z           web  host2  us_west         15

三、优化模型设计

设计您的模型以实现更简单且性能更高的查询。 遵循设计指南,使您的模式易于查询。 了解这些指南如何带来更高性能的查询。

为查询而设计,保持measurements和keys简单

下面的模式演示了易于查询的测量值标签键字段键。

measurementtagtagfieldfield
airSensor传感器ID车站湿度温度
waterQualitySensor---传感器ID车站酸碱度温度

airSensor和模式waterQualitySensor说明了以下准则:

waterQualitySensor,sensorId=W0101,station=Harbor pH=6.1,temperature=16.103 1472515200000000000

使用tags和fields

influxdb会为tag建立索引,而field未建立索引,这意味着查询tag比查询field性能更高。
### [使用字段来存储唯一的数字数据](https://docs.influxdata.com/influxdb/v2/write-data/best-practices/schema-design/#use-fields-for-unique-and-numeric-data)

使用field来存储唯一的数字数据

  • 将唯一或经常更改的值存储为字段值。
  • 将数值存储为字段值。 (tag仅用于存储字符串)

使用tag提高查询性能

如果可以合理索引,则将值存储为tag。 如果值用于 filter() 或 group() 函数,则将值存储为tag。 如果值在多个数据点之间共享,则将值存储为tag,即有关字段的元数据。 由于 InfluxDB 对tag进行索引,因此查询引擎不需要扫描存储桶中的每条记录来定位标签值。 例如,考虑一个存储有关数千个用户的数据的存储桶。 由于 userId 存储在字段中,因此查询用户 abcde 需要 InfluxDB 扫描每一行中的 userId。 要更快地检索数据,请过滤标签以减少扫描的行数。 标签应该存储可以合理索引的数据。 以下查询按公司标签进行筛选,以减少扫描 userId 的行数。

保持key简单

除了保持密钥不含数据之外,请遵循以下附加指南以使其更易于查询: 避免关键字和特殊字符 避免标签和字段的重复名称 避免在键中使用关键字和特殊字符 为了简化查询编写,请勿在标记和字段键中包含保留关键字或特殊字符。 如果你在key中使用 Flux 关键字,那么你必须将键用双引号括起来

四、解决高基数

如果对 InfluxDB 的读取和写入开始变慢,则高基数可能会导致内存问题。因此在谁急时需要解决高基数的问题。

高基数的原因

每个唯一的索引数据元素集形成一个系列键。 包含高度可变信息(例如唯一 ID、哈希值和随机字符串)的tag大量序列,也称为高序列基数。 高系列基数是许多数据库工作负载内存使用率较高的主要驱动因素。

测量基数


influxdb.cardinality():Flux 函数,返回数据中唯一系列键的数量。

SHOW SERIES CARDINALITY:InfluxQL 命令,返回数据中唯一系列键的数量。

解决高基数的问题

要解决高系列基数问题,请完成以下步骤(对于多个存储桶,如果适用): 1.review tag 2.优化模型 3.删除高基数数据

review tag

检查您的以确保每个标签不包含大多数条目的唯一值: 扫描您的标签以查找常见标签问题。 使用下面的示例 Flux 查询来计算唯一标签值。

通用的tag issues

查找以下常见问题,这些问题通常会导致许多独特的标记值: 将日志消息写入标签。 如果日志消息包含唯一时间戳、指针值或唯一字符串,则会创建许多唯一标记值。 将时间戳写入标签。 通常是在客户端代码中意外完成的。 随着时间的推移而增长的唯一标签值 例如,用户 ID 标签可能在小型初创公司中有效,但当公司发展到拥有数十万用户时可能会开始引起问题。

统计唯一的tag values

以下示例 Flux 查询显示了哪些标签对基数贡献最大。 寻找值比其他值高几个数量级的标签。

// Count unique values for each tag in a bucket
import "influxdata/influxdb/schema"

cardinalityByTag = (bucket) => schema.tagKeys(bucket: bucket)
    |> map(
        fn: (r) => ({
            tag: r._value,
            _value: if contains(set: ["_stop", "_start"], value: r._value) then
                0
            else
                (schema.tagValues(bucket: bucket, tag: r._value)
                    |> count()
                    |> findRecord(fn: (key) => true, idx: 0))._value,
        }),
    )
    |> group(columns: ["tag"])
    |> sum()

cardinalityByTag(bucket: "example-bucket")

如果您遇到基数失控,上面的查询可能会超时。 如果您遇到超时,请一次运行一个查询。 1、获取到所有标签

// Generate a list of tags
import "influxdata/influxdb/schema"

schema.tagKeys(bucket: "example-bucket")

2、计算每个标签的唯一标签值:

// Run the following for each tag to count the number of unique tag values
import "influxdata/influxdb/schema"

tag = "example-tag-key"

schema.tagValues(bucket: "my-bucket", tag: tag)
    |> count()

这些查询应有助于识别每个存储桶中高基数的来源。 要确定哪些特定标签正在增长,请在 24 小时后再次检查基数,看看一个或多个标签是否显着增长。