InfluxDB 入门(下):读懂 Line Protocol,并写入第一批时序数据
上一篇里,我们把一条时序数据拆成了四部分:
1 | Point = Measurement + Tag Set + Field Set + Timestamp |
写入 InfluxDB 时,这四部分通常会挤进一行 Line Protocol:
1 | weather,city=Beijing,sensor=s01 temperature=26.5,humidity=58i,status="normal" 1724659200000000000 |
它没有 JSON 的花括号,也没有 SQL 的 INSERT INTO,全靠逗号、空格和等号划分结构。把上一篇的模型放回来,这一行并不难读:
1 | weather Measurement |
这篇把容易写错的地方拆开讲清,并用 Docker 启动 InfluxDB 3 Core,走完创建 Database、写入和 SQL 查询这一小圈。
先把版本说清楚
本文的环境固定为:
1 | InfluxDB 3 Core 3.11.2 |
示例固定使用镜像:
1 | influxdb:3.11.2-core |
固定版本是为了给命令划清边界。InfluxDB 1.x、2.x 和 3.x 的存储容器、认证、CLI 与查询语言都有差异;正在使用 2.x 的话,不要直接复制这里的 3.x 命令。
Line Protocol 只描述待写入的 Point。创建 Database 和 Token 交给 influxdb3 CLI,数据通过 CLI 或 HTTP API 发送,查询使用 SQL。它和 SQL 各管一段链路。
一行协议是怎样分段的
Line Protocol 的基本结构如下:
1 | <measurement>[,<tag_key>=<tag_value>...] <field_key>=<field_value>[,<field_key>=<field_value>...] [timestamp] |
这串语法实际只有三个分区:
1 | Measurement 和 Tag Set | Field Set | Timestamp |
它们之间使用未转义的空格分隔:
1 | weather,city=Beijing,sensor=s01 temperature=26.5,humidity=58i 1724659200000000000 |
第一个空格结束 Measurement 和 Tag Set,第二个空格结束 Field Set。Timestamp 可以省略,因此第二个空格不一定存在;第一个不能少,Field 也至少要有一个:
1 | weather temperature=26.5 |
这行没有 Tag,只有一个 Float Field;Timestamp 由服务端按接收数据时的当前 UTC 时间补上。
空格不是为了排版
这种写法在人看来更松快,却会破坏分区:
1 | weather, city=Beijing temperature = 26.5 |
解析器会把第一个未转义空格当成分区边界,逗号或等号旁不能随意留空。应写成:
1 | weather,city=Beijing temperature=26.5 |
格式虽然简单,分隔符本身就是语法。
空格前:Measurement 和 Tag Set
一行最前面的 weather 是 Measurement,在 InfluxDB 3 中对应一张 Table:
1 | weather,city=Beijing temperature=26.5 |
1 | SELECT * FROM weather; |
Measurement 后有 Tag 时,第一个 Tag 前加逗号;没有 Tag,就直接用空格进入 Field Set:
1 | weather,city=Beijing temperature=26.5 |
city 和 sensor 是两个 Tag:
1 | weather,city=Beijing,sensor=s01 temperature=26.5 |
每个 Tag 都是 tag_key=tag_value,多个 Tag 之间只用逗号:
1 | city=Beijing,sensor=s01 |
Tag Value 一律按字符串处理,不要照着 JSON 加双引号。引号可能成为值本身的一部分:
1 | weather,city="Beijing" temperature=26.5 |
在 InfluxDB 3 中,Tag 和时间列共同构成表的主键,Tag 的先后还会影响物理排序。第一次写入新表前应定好 Schema。若查询通常先看 city、再看 sensor,就统一写成:
1 | weather,city=Beijing,sensor=s01 ... |
别让一部分客户端写 city,sensor,另一部分写 sensor,city。Series 的逻辑身份不依赖这段文本的顺序,但首次写入顺序会进入 InfluxDB 3 的表结构和物理排序。
空格后:Field 类型写在值里
第一个空格后面是 Field Set:
1 | weather,city=Beijing temperature=26.5,humidity=58i,status="normal",online=true |
多个 Field 也用逗号分隔。它们的类型没有单独声明,值怎么写,决定了这一列是什么类型:
1 | temperature=26.5 # Float64 |
最容易漏掉的是整数后缀。58 和 58i 看起来只差一个字母,落到 Schema 里却是两种类型:
1 | value=58 # Float64 |
有符号整数用 i,无符号整数用 u;无符号整数不能表示负数。String Field 必须用双引号,Boolean 则不加:
1 | status="normal" |
online="true" 保存的是字符串。类似地,status=normal 可以出现在 Tag Set,却不是合法的 String Field Value。位置和写法都参与数据建模。
同一列的类型要保持一致。第一次写入:
1 | weather temperature=26.5 |
InfluxDB 3 会把 temperature 建成浮点数列。后续若改成:
1 | weather temperature="26.5" |
它就会与已有 Schema 冲突,写入可能被拒绝。从 JSON、CSV 或消息队列转换数据时,采集端需要先把类型统一好。
Timestamp:别让单位靠猜
一行最后的整数是 Timestamp:
1 | weather,city=Beijing temperature=26.5 1724659200000000000 |
它是 Unix 时间戳,但数字本身没有单位。秒、毫秒、微秒还是纳秒,要由写入接口的 precision 参数决定。
常见精度是:
| 精度 | CLI 参数 | v3 HTTP API 参数 |
|---|---|---|
| 秒 | s | second |
| 毫秒 | ms | millisecond |
| 微秒 | us | microsecond |
| 纳秒 | ns | nanosecond |
InfluxDB 3 Core 的 v3 写入接口还支持 auto,按时间戳数量级推测精度。手工试验可以用,采集程序最好明确指定。例如 Shell 的:
1 | date +%s |
产生秒级时间戳,CLI 应配 --precision s,/api/v3/write_lp 则配 precision=second。两套拼写不能混用。
如果 Timestamp 省略,InfluxDB 使用服务端接收数据时的当前 UTC 时间。测试时可以这样做,采集程序则最好明确传入事件真正发生的时间。
每分钟采一次温度,秒或毫秒已经足够。官方建议使用能满足需求的最粗精度,这也有利于压缩。按纳秒解释时,64 位时间戳可表达的日期大致在 1677 年到 2262 年之间;日常更常见的故障仍是单位配错,数据因此落到意外的日期。
转义:反斜杠用在哪
名称和值只含字母、数字、短横线和下划线时,通常无需转义。遇到分隔字符,Line Protocol 用反斜杠保住它们。
Measurement 中需要转义逗号和空格:
1 | environment\ sensors temperature=26.5 |
它们分别表示 environment sensors 和 weather,indoor。
Tag Key、Tag Value 和 Field Key 多一个等号要处理,因此逗号、等号、空格都要转义:
1 | weather,room=Living\ Room temperature=26.5 |
第三行的 room temperature 是一个 Field Key。String Field Value 则转义双引号和反斜杠:
1 | event note="sensor said \"hot\"" |
换行用于分隔 Point,不能直接塞进字符串。也别沿用 SQL 的习惯给名称套引号,下面这些引号可能成为名称或值的一部分:
1 | "weather","city"="Beijing" "temperature"=26.5 |
简单、稳定的命名能省掉大部分转义;只有名称确实带分隔字符时才使用反斜杠。
多行就是一批数据
一行表示一个 Point,多行就是多个 Point:
1 | weather,city=Beijing,sensor=s01 temperature=26.5,humidity=58i 1724659200 |
换行既是 Point 的边界,也是批量写入的基础。相比每条数据都发一个 HTTP 请求,合成一批通常更高效。InfluxDB 3 Core 的写入优化文档建议从约 10000 行或 10 MB 的批次开始考虑,以先达到者为准;这不是最低门槛,只是提醒高频采集程序不要每来一个 Point 就新建一次请求。
重复 Point 不是可靠的更新操作
一个 Point 的逻辑身份由三部分决定:
1 | Table + 完整 Tag Set + Timestamp |
这两次写入的身份完全相同:
1 | weather,city=Beijing temperature=26.5 1724659200 |
重复写入中不重名的 Field 会按并集合并;上面两行都写了 temperature,才形成同名 Field 冲突。InfluxDB 2.x 的 TSM 引擎有确定的覆盖规则;InfluxDB 3 Core 官方文档明确提醒,冲突值的覆盖结果并不确定,查询和最终持久化都不保证留下“最后写入”的值。
因此第二次写入不能当作可靠的 UPDATE。两次不同观测应使用各自真实的 Timestamp;增加 Field 也无法让 Point 唯一,因为 Field 不属于主键。
用 Docker 启动 InfluxDB 3 Core
拉取本文固定使用的镜像:
1 | docker pull influxdb:3.11.2-core |
启动容器:
1 | docker run -d \ |
服务监听本机 8181,数据写进 Docker Volume,并以本地文件作为 Object Store。node-id 也固定下来。
端口只绑定 127.0.0.1。首个管理员 Token 创建前,初始化接口允许未认证调用;刚启动就暴露到不可信网络,别人可能抢先创建拥有完整权限的 Token。
可以检查容器是否还在运行:
1 | docker ps --filter name=influxdb3-core |
若容器已经退出,查看日志:
1 | docker logs influxdb3-core |
这里不粘贴固定输出:镜像补丁版本、Docker 版本和启动时间都会改变日志。只需确认容器保持运行,并监听本机 8181 端口。
创建管理员 Token
首次初始化 InfluxDB 3 Core,需要创建 Admin Token:
1 | docker exec influxdb3-core \ |
Token 明文只在创建时显示一次,要立即保存。这个 Token 拥有完整管理权限且不会过期,不能硬编码进脚本、镜像或 Git 仓库,也不该分发给所有采集程序。本地实验可先放进当前 Shell 的环境变量:
1 | export INFLUXDB3_AUTH_TOKEN='把刚才返回的 Token 粘贴到这里' |
直接输入可能让 Token 留在 Shell 历史中。生产环境应交给 Secret 管理工具,并配合网络隔离。InfluxDB 3 Core 3.11.2 暂不提供资源级的最小权限 Token;可以为应用创建带过期时间的 Named Admin Token,但它仍有完整管理权限。
创建第一个 Database
创建一个名为 tutorial 的 Database:
1 | docker exec influxdb3-core \ |
若实验数据最多保留 30 天,可在创建时指定 Retention Period:
1 | docker exec influxdb3-core \ |
两条创建命令选一条即可。Retention Period 决定数据能保留多久,和 SQL 的查询时间范围无关:查询最近一小时不会删除旧数据,保留 30 天也不等于每次都扫描 30 天。
InfluxDB 3 Core 默认把单次查询可访问的 Parquet 文件数限制为 432。按默认每 10 分钟生成一代文件估算,大约对应 72 小时的数据范围;它可以是近期或历史区间,并非“只能查最近 72 小时”。--query-file-limit 可以调高,但会增加内存、对象存储请求和 OOM 风险。本文只查最近一小时,不会碰到这个边界。
用 CLI 写入第一条数据
生成当前的秒级 Unix 时间戳:
1 | NOW=$(date +%s) |
把它放进一个天气 Point:
1 | docker exec influxdb3-core \ |
--precision s 与 date +%s 对应,明确告诉 InfluxDB 最后的 Timestamp 按秒解释。
如果命令成功但没有打印一张数据表,这很正常。写入操作的目标是接收数据,是否真的写对了,要通过查询验证。
用 SQL 查询刚刚写入的数据
用 SQL 读回来:
1 | docker exec influxdb3-core \ |
这里可以看到 Line Protocol 与 SQL 的对应关系:
| Line Protocol | SQL 中看到的内容 |
|---|---|
Measurement weather | Table weather |
Tag city | city 列 |
Tag sensor | sensor 列 |
Field temperature | temperature 列 |
Field humidity | humidity 列 |
| Timestamp | time 列 |
Line Protocol 写入时没有列清单,但各部分最终都会落到对应的表结构中。
通过 HTTP API 批量写入
采集程序通常通过 HTTP API 写入。为后面的聚合查询,在上一个完整的 5 分钟窗口内准备两个时间点:
1 | NOW=$(date +%s) |
WINDOW_START 是上一个完整窗口的起点。用 /api/v3/write_lp 一次发送四个 Point:
1 | curl --request POST \ |
HTTP API 的精度写作 precision=second,Token 放在 Authorization: Bearer ... 请求头中。@- 从标准输入读取数据,<<EOF 会展开 Shell 变量。这里应保留 --data-binary:普通 --data @- 可能改动换行,而批量 Line Protocol 正靠换行分隔 Point。
/api/v3/write_lp 默认允许部分写入。如果某一行格式错误,请求会返回 400,但合法行可能已经落库。此时先看响应里的逐行错误,不要直接假设整批都没写;希望包含错误行时整批拒绝,可以在 URL 中加上 accept_partial=false。
查询一个城市最近一小时的数据
先只看北京:
1 | docker exec influxdb3-core \ |
city Tag 负责过滤,time 限定范围,两个 Field 负责承载查询结果。若结果为空,可以先用 ORDER BY time DESC LIMIT 10 看最新记录;常见原因是 Timestamp 精度不一致,数据落到了错误的日期。
每 5 分钟计算一次平均温度
这条 SQL 把最近一小时的数据划入 5 分钟窗口,并按城市计算平均温度:
1 | docker exec influxdb3-core \ |
DATE_BIN 把 Timestamp 对齐到 5 分钟窗口的起点:
1 | [17:00:00, 17:05:00) → 17:00:00 窗口 |
GROUP BY window_start 合并同一窗口,city 继续区分城市,AVG(temperature) 对每组温度求平均。这对应上一篇的查询心智模型:
1 | 时间范围 + Tag 分组 + Field 聚合 |
别名不要写成 AS time 再 GROUP BY time,否则 SQL 会优先指向原表的 time 列。单独使用 window_start 可以避开这层歧义。
这批数据留下的 Schema
写入完成后,weather 表的结构是:
1 | weather |
需要按传感器型号过滤时,可以增加 model Tag:
1 | weather,city=Beijing,sensor=s01,model=x100 temperature=26.5,humidity=58i <timestamp> |
不参与查询的备注则放进 String Field:
1 | weather,city=Beijing,sensor=s01 temperature=26.5,humidity=58i,note="window opened" <timestamp> |
每一列都该有明确用途。InfluxDB 3 中的高基数 Tag 已不是旧版 TSM 架构里的同一种性能禁区,设备 ID、跟踪 ID 若用于精确筛选,可以成为 Tag;从不参与查询的长随机字符串,仍没必要放进主键。
几个常见问题怎样排查
写入时报协议解析错误
先检查分区空格:Measurement/Tag 与 Field 之间必须有一个;写了 Timestamp 时,Field 和 Timestamp 之间再有一个。
1 | Measurement,TagSet FieldSet Timestamp |
其余常见原因包括:
- Tag 和 Field 之间是否遗漏空格;
- 逗号或等号旁是否多了空格;
- String Field 是否使用双引号;
- 整数是否带有
i或u后缀; - 名称中的空格、逗号和等号是否正确转义。
写入成功,但查询不到数据
先查询 ORDER BY time DESC LIMIT 10,不要马上加复杂条件。
仍然看不到时,逐项确认:
- CLI 和 HTTP 请求是否写进同一个 Database;
- Token 是否有对应权限;
- 时间戳是秒还是纳秒;
- 查询的时间范围是否覆盖数据时间;
- 时钟是否严重偏差。
返回认证错误
确认请求头使用了当前 InfluxDB 3 Core 接受的格式:
1 | Authorization: Bearer <token> |
还要确认 Shell 变量确实存在:
1 | test -n "$INFLUXDB3_AUTH_TOKEN" && printf 'Token variable is set\n' |
这条检查只判断变量是否为空,不会把 Token 打印到终端。
InfluxDB 2.x 和 3 Core 不要混着抄
网上大量教程仍然使用 InfluxDB 1.x 或 2.x。它们不是全部失效,但需要先认出版本。
| 概念 | InfluxDB OSS 2.x | InfluxDB 3 Core |
|---|---|---|
| 外层组织 | Organization | 没有对应的 Organization 层 |
| 主要存储容器 | Bucket | Database |
| 数据类别 | Measurement | Table,Line Protocol 中仍写 Measurement |
| 主要查询语言 | Flux,也有兼容的 InfluxQL | SQL 和 InfluxQL,不支持 Flux |
| CLI | influx | influxdb3 |
| 原生写入 API | /api/v2/write | /api/v3/write_lp |
| 常见存储架构 | TSM / TSI | Arrow、DataFusion、Parquet 与 Object Store |
InfluxDB 3 仍然提供部分 v1、v2 兼容 API,所以偶尔会看到这样的地址:
1 | /api/v2/write?bucket=... |
它可以与 InfluxDB 3 的兼容层一起使用,但这不代表 3.x 又恢复了完整的 Organization 和 Bucket 数据模型。兼容的是请求形态,不是完整数据模型;通过兼容 API 写入时,表的 Tag 定义不可变,同一张表里的 Tag 和 Field 也不能同名。
判断一篇旧教程能不能直接用,至少先看四个地方:
- 它启动的是
influxd还是influxdb3; - 它写的是 Bucket 还是 Database;
- 它查询时使用 Flux、InfluxQL 还是 SQL;
- 它访问的是
/api/v2/write还是/api/v3/write_lp。
只要这几项不在同一版本里,就不要继续无脑复制。
实验结束后怎样清理
只想停止容器,可以执行:
1 | docker stop influxdb3-core |
之后还可以重新启动,数据仍然保存在 Docker Volume 中:
1 | docker start influxdb3-core |
如果确认整个实验都不再需要,可以删除容器:
1 | docker rm -f influxdb3-core |
docker rm -f 只删除容器,不会删除命名卷。如果连数据也不再需要,再执行下面的命令。它会删除 Volume 中的 InfluxDB 数据,无法通过重新启动容器找回;执行前务必确认 influxdb3-data 只存放本文的实验数据:
1 | docker volume rm influxdb3-data |
误删仍有用的数据卷,里面的数据就无法找回。
总结:Line Protocol 就是心智模型的文本形式
回到开头的 Line Protocol:
1 | weather,city=Beijing,sensor=s01 temperature=26.5,humidity=58i,status="normal" 1724659200000000000 |
第一个空格前是 Measurement 和 Tag,第二段是有类型的 Field,末尾是与 precision 对应的 Timestamp。逗号分隔同一区域里的 Tag 或 Field,换行分隔一批数据里的 Point。
整个写入和查询链路可以收成这样:
1 | 一次业务观测 |
不必背下所有转义规则。先记住分区空格、整数后缀和 Timestamp 精度:至少一个空格把 Field 分出来,存在 Timestamp 时才需要第二个;后两项决定列类型和数据落在时间轴的什么位置。重复写入同一个 Tag Set 与 Timestamp,也不能拿来冒充可靠的更新。
参考资料
- InfluxDB 3 Core Documentation: Install InfluxDB 3 Core
- InfluxDB 3 Core Documentation: Set up InfluxDB
- InfluxDB 3 Core Documentation: Line protocol
- InfluxDB 3 Core Documentation: Write using the v3 API
- InfluxDB 3 Core Documentation:
influxdb3 write - InfluxDB 3 Core Documentation: Query using SQL and the HTTP API
- InfluxDB 3 Core Documentation: Query data
- InfluxDB 3 Core Documentation:
DATE_BIN - InfluxDB 3 Core Documentation: Optimize writes
- InfluxDB 3 Core Documentation: Schema design
- InfluxDB 3 Core Documentation: Migrate from InfluxDB 1.x and 2.x
- curl Documentation: Post binary data
- 标题: InfluxDB 入门(下):读懂 Line Protocol,并写入第一批时序数据
- 作者: Kaku
- 创建于 : 2026-08-26 17:51:00
- 更新于 : 2026-08-26 18:33:46
- 链接: https://www.kakunet.top/2026/08/26/InfluxDB-入门(下):读懂-Line-Protocol,并写入第一批时序数据/
- 版权声明: 本文章采用 CC BY-NC-SA 4.0 进行许可。