一、优化对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 倍
- 在telegraf中使用gzip压缩
- 客户端库中使用gzip压缩
- 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简单
| measurement | tag | tag | field | field |
|---|---|---|---|---|
| airSensor | 传感器ID | 车站 | 湿度 | 温度 |
| waterQualitySensor | ---传感器ID | 车站 | 酸碱度 | 温度 |
airSensor和模式waterQualitySensor说明了以下准则:
- 每个measurement都是一个描述模式的简单名称。
- key在模型中不会重复。
- key不使用保留关键字或特殊字符。
- 标签(
sensorId和station)存储跨多个数据点的通用元数据。 - 字段(
humidity、、pH和temperature)存储数字数据。 - 字段存储唯一或高度可变的数据。
- measurement和key不包含数据;tag和field将存储数据。
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 小时后再次检查基数,看看一个或多个标签是否显着增长。