InfluxDB(二)——内存原理核心概念以及与MySQL差异通俗解析
前言:最初动笔,本想直接分享InfluxDB的实用使用技巧,帮大家避开实操中的坑、提升效率。但在查阅各类相关文章后,我发现很多内容都停留在表面,不仅没能讲透核心逻辑,还存在不少常见误区——最典型的就是大家习惯性地把关系数据库(比如MySQL)的使用思维,套用到InfluxDB这类时序数据库上,忽略了两者的本质区别,导致写出来的配置、执行的查询要么效率低下,要么根本不符合时序数据的存储规律
目录
1. InfluxDB 1.x 写入流程 (TSM Engine)
2. InfluxDB 2.x 写入流程 (TSM Engine + OSS)
3. InfluxDB 3.x 写入流程 (IOx/Arrow Engine)
第一步:数据分片(Sharding / Partitioning)
这其中,除了大家对“时序数据库”的核心逻辑理解不深,也和InfluxDB的版本差异有很大关系。不同版本的InfluxDB,在核心概念、操作方式上有不少区别,比如1.x版本的核心概念,到了2.x版本就有明显的整合和替换
与其直接分享零散的使用技巧,不如先从最基础的核心概念讲起。毕竟技巧是“术”,概念是“道”,只有把基础概念理解透彻,分清InfluxDB和关系数据库的本质不同
本文不会堆砌专业术语,会以通俗的方式全程结合实际使用场景,帮大家建立对时序数据库的正确认知,为后续的技巧分享打下基础
本司目前使用的是InfluxDB1.6.8,目前文章使用的是Influx2.7.5,1.x没有UI界面,各公司根据场景不同选择的版本就会不同,为了更全面的熟悉各版本的差异,还是需要了解写入原理的
一、InfluxDB写入原理
PS:如果觉得原理晦涩难懂的,有点偏概念性,可以先从(二、InfluxDB数据导入)实践开始看起,回头再看写入原理会更好理解一点
1. InfluxDB 1.x 写入流程 (TSM Engine)
官方文档:https://docs.influxdata.com/enterprise_influxdb/v1/
核心:WAL + Cache + TSM 文件,经典时序数据库架构

流程步骤:
- 写入 WAL:数据先写入磁盘上的预写日志,保证崩溃不丢数据
- 写入内存 Cache:再写入内存缓存,对外返回写入成功,提升性能
- 后台异步刷盘:Cache 中的数据按时间 / 大小策略,后台写入 TSM 持久化文件
- 合并压缩:后台 Compaction 合并多个小 TSM 文件,优化存储和查询性能
2. InfluxDB 2.x 写入流程 (TSM Engine + OSS)
官方文档:https://docs.influxdata.com/influxdb/v2/get-started/
核心:底层存储引擎和 1.x 一致,只是上层架构和 API 有变化,2.x 增加了 Bucket/Organization 等概念,引入了索引缓存(Index Cache)优化查询性能

3. InfluxDB 3.x 写入流程 (IOx/Arrow Engine)
核心:架构大改,从 TSM 切换到了基于 Apache Arrow/Parquet 的 IOx 引擎,引入了 Read Buffer 读缓存,并且完全兼容 SQL,底层存储从 TSM 改为 Parquet,适合云原生和大数据生态
流程步骤:
- 写入 WAL:依然保留预写日志,保证数据不丢
- 写入 MemTable:写入内存表(MemTable),而不是原来的 Cache
- 后台刷盘:MemTable 数据刷写成 Parquet 列存文件
- 分层合并:采用 LSM 树式分层合并,优化列存文件,提升压缩率和查询性能
这里大概了解一下,简而言之就是
InfluxDB 1.x 和 2.x 在核心存储引擎层面(TSM引擎)的写入路径是完全一致的。它们都遵循 WAL → Cache → TSM文件,数据先顺序写入预写日志(WAL)保证持久性,同时更新内存中的Cache(缓存),随后Cache中的数据会异步刷新到磁盘,生成小的 TSM 文件,后台压缩器(Compactor)会持续将多个小的 TSM 文件合并成更大的 TSM 文件,以减少文件数量、优化索引和查询性能
而InfluxDB 3.x是是WAL → MemTable → Parquet文件,数据同样写入WAL,立即写入内存中的写缓冲(Buffer),当Buffer到达一定的大小或时间阈值,系统会触发一个“快照(Snapshot)”操作,将内存中数据直接转换成一个不可变的 Parquet文件,后台压缩器(Compactor)会将多个小的 Parquet 文件合并成更大的 Parquet 文件,以提升查询性能和压缩率。
各个版本的差异就是:
| 特性 | 1.x & 2.x (TSM引擎) | 3.x (IOx/Arrow引擎) |
|---|---|---|
| 写入路径 | WAL → Cache → TSM文件 | WAL → Buffer (Arrow) → Parquet文件 |
| 内存结构 | Cache (行式,未压缩) | Buffer (列式,Arrow格式) |
| 持久化格式 | TSM文件 (列式存储) | Parquet文件 (标准列存格式) |
| 合并机制 | Compaction 合并 TSM 文件 | Snapshot (生成Parquet) + Compaction (合并Parquet) |
二、InfluxDB数据导入
InfluxDB有很多概念:Bucket、Measurement、Tag、Field、Point、Series、Shard、RP、CQ/Task,就不单一堆砌晦涩难懂的概念了,一般使用InfluxDB的开发人员只了解红色字体的关键词,所以就从此切入吧
进入InfluxDB容器内部使用Influx Cli命令导入,参考官方文档提供的示例数据,先在
1. 创建Token
InfluxDB Web Ui创建一个长期的Token

可以先测试一下是否能进入控制台
docker exec -it influxdb2 bash
# 容器内执行 influx 命令(如查看版本)
influx version
# 退出容器
exit
2. 配置InfluxDB
将上面创建的Token赋值到链接中
influx config create --config-name onboarding \
--host-url "http://localhost:8086" \
--org "Your org" \
--token "Your Token" \
--active
关于Org可以从上一篇文章的Docker run配置的Org中获取

或者从Lood Data——Sources——Influx CLI中直接复制当前命令,它已经自动获取当前组织Org的ID,也会创建临时的Token,如果需要长期使用,需要自行创建Token

3. 写入数据
① Line Prtocol 行协议
在上一篇文章InfluxDB——一个高效处理数据的时序数据库,如何根据InfluxDB2.x的Line Prtocol写了数据,数据太少了
行协议的格式:

数据格式是:
user,name=echola age=22 1776242815
user,name=leo age=33 1776242829
user,name=jenny age=34 1776242829
结构分解:
Measurement: user
Tag: name=echola/leo/jenny
Field: age=22/33/34
Timestamp: 1776242815/1776242829
② CSV
直接下载CSV,通过Lood Data——Sources——Upload a CSV的形式导入

官方还有以下示例数据,下载CSV:https://github.com/influxdata/influxdb2-sample-data/tree/master

下载之后关键部分的CSV格式数据是:

明显和行协议Line Prtocol的格式是有区别的
数据:
_time,_value,_field,_measurement,sensor_id
2026-04-16T01:01:41Z,0.5010463,co,airSensors,TLM0100
2026-04-16T01:40 :51Z,0.4996955,co,airSensors,TLM0101
2026-04-16T01:01:01Z,35.047629,humidity,airSensors,TLM0100
2026-04-16T01:40:11Z,35.018285,humidity,airSensors,TLM0101
2026-04-16T01:01:21Z,71.986109,temperature,airSensors,TLM0100
2026-04-16T01:40:31Z,71.971565,temperature,airSensors,TLM0101
结构分解:
| 列名 | 类型 | 说明 | 示例值 |
|---|---|---|---|
_time |
时间戳 | 数据点时间 | 2026-04-16T01:01:41Z |
_measurement |
字符串 | 表名/度量名称 | airSensors |
_field |
字符串 | 字段名(动态的) | co, humidity, temperature |
_value |
多种类型 | 字段值(对应的数值) | 0.5010463, 35.047629, 71.986109 |
sensor_id |
字符串 | Tag列(自定义标签) | TLM0100, TLM0101, TLM0102 |
可以看出有两种不一样的形式
🔴 重点:
InfluxDB 在导入 CSV 数据时,底层并非直接存储 CSV 格式,而是会按照 CSV 中的表头、注释(
#datatype、#group、_measurement、_field、_value、tag等)自动映射并转换为 Line Protocol 格式写入
如果把上述CSV的数据转为行协议的格式:
airSensors,sensor_id=TLM0100 co=0.5010463,humidity=35.047629,temperature=71.986109 1744851701
airSensors,sensor_id=TLM0101 co=0.4996955,humidity=35.018285,temperature=71.971565 1744854451
在InfluxDB中,同一时刻,同一个传感器的temperature、humidity、co属于同一批采集数据
CSV里虽然把它们拆成了多行(不同时间、不同行),但真正写入时序库时,必须让三个指标拥有完全想用的时间戳,才能形成一个完整的数据点,确保后续查询、聚合、图表展示时能正确对应
总结
- CSV = 易读、带注释、带表头
- Line Protocol = 高效、机器写入
③ Influx CLI
将官方提供的空气传感器数据导入Bucket中,只会下载今天的数据,空气质量传感器样本数据代表了一个“物联网”(IoT)用例,通过模拟建筑中多个房间的温度、湿度和一氧化碳水平。
influx write --bucket echola-bucket --url https://influx-testdata.s3.amazonaws.com/air-sensor-data-annotated.csv
如果想要持续下载并将更新的空气质量传感器样本数据写入Bucket中,需要创建一个Task,就会自动采集数据到指定的Measurement,可以参照官方文档链接:https://docs.influxdata.com/influxdb/v2/reference/sample-data/#air-sensor-sample-data

可以从Data Explorer的echola-bucket中看到数据已经导入进来了

可以看到CSV数据格式是如此

三、InfluxDB核心概念
上面已经三种方式对时序数据进行导入,相信已经有一定的了解了,再详细说一下目前接触到的Bucket、Measurement、Tag、Field、Time的概念
1. Bucket(桶)
- Bucket:可以理解为存储数据的容器,相当于MySQL的数据库DataBase+数据保留策略(Retention Policy)【这个概念下篇再说】,不在行协议中体现,本文就是echola-bucket
2. Measurement(度量)
- Measurement:Measurement 是 Bucket 中的表,表示一类相似的数据集合,相当于MySQL的表名Table,就是airSensors
3. Tag(标签)
- Tag:索引字段,用于高效查询和分组。格式为键值对(
key=value),通常是字符串类型。示例中的sensor_id=TLM0100和sensor_id=TLM0101是 Tag,用于区分不同传感器的数据,Tag都是String类型
4. Field(字段)
- Field: 是实际存储的数值数据,类型可以是float、int、string或bool。示例中的
co=0.5010463(一氧化碳浓度)、humidity=35.047629(湿度)、temperature=71.986109(温度)均为 Field
Tag和Field都是存储数据值,区别就是Tag分区,Field过滤
Tag建索引,使用于低基数,筛选分组字段,Field适用于业务数据,高基数的唯一标识,设计原则:Tag越少越好
5. TimeStamp(时间戳)
- TimeStamp(时间戳) :是时间戳,表示数据点的时间。示例末尾的
1744851701和1744854451是 Unix 时间戳(单位:纳秒或秒,取决于配置),标记数据生成的具体时间
6. Point(数据点)
在 InfluxDB 中,一条数据(Point)的唯一性由以下四者共同决定:
Point = Measurement + Tag Set + Field Key + Timestamp
参照数据会更好理解一点:
airSensors,sensor_id=TLM0100 co=0.5010463,humidity=35.047629,temperature=71.986109 1744851701
7. Series(时间线)
- 这是最关键的概念!!!后面要考!
Series = Measurement +Tags
❓Series 为什么超级重要?
因为:
-
InfluxDB 每一个 series 都会占用内存
-
索引、查询、分片、压缩、内存占用 全都以 series 为单位
-
series 数量 = 时序库的压力核心
Series太多-> 内存爆炸、查询OOM、写入变慢
数据:
# 第一条
cpu,host=server1,region=sz usage_idle=92.1 1620000000000
# 第二条
cpu,host=server1,region=sz usage_idle=91.5 1620000010000
# 第三条
cpu,host=server2,region=sz usage_idle=85.2 1620000000000
从上面按照行协议的格式
可以看出cpu是Measurement,host和region是Tag,而usage_idle是flied,也就是以下公式,其中Field和TimeStamp不参与Series
Series = measurement + tag1=v1 + tag2=v2 + ...
那么上面数据就有2个Series
第一条、第二条:measurement 相同 + Tag 完全相同 → 同一个 series
第三条:host 不同 → 新的 series
其他术语可以参照官方文档:https://docs.influxdata.com/influxdb/v2/reference/glossary/
四、与关系型的数据库MySQL的区别
那InfluxDB与传统数据库MySQL有什么不同呢?最根本的差异在于
MySQL 是行式存储,而 InfluxDB 是列式存储
还是使用空气传感器的数据:
airSensors,sensor_id=TLM0100 co=0.5010463,humidity=35.047629,temperature=71.986109 1744851701
airSensors,sensor_id=TLM0101 co=0.4996955,humidity=35.018285,temperature=71.971565 1744854451
1. MySQL的存储方式
如果在MySQL中,存入air_sensors表中:
①逻辑存储
| id | sensor_id | co | humidity | temperature | time |
|---|---|---|---|---|---|
| 1 | TLM0100 | 0.5010463 | 35.047629 | 71.986109 | 2025-04-17 07:41:41 |
| 2 | TLM0101 | 0.4996955 | 35.018285 | 71.971565 | 2025-04-17 08:27:31 |
②物理存储(InnoDB 引擎)
MySQL 按行连续存储在 16KB 的 Page数据页里,由下图可以看出字段都是存储在一起的,读一个数据点,必须把整行所有字段一起读出来,一行 = 头部信息 + 所有字段值连续字节流,并没有分开存储
[MySQL InnoDB 数据页 Page #100 的物理布局]
┌─────────────────────────────────────────────────────────────────────────┐
Page Header │ 行记录 1 │ 行记录 2 │ 行记录 3 │ ... │ Free Space │ Footer │
└─────────────────────────────────────────────────────────────────────────┘
放大看 "行记录 1" 的物理字节流:
┌──────────────┬─────────┬───────────┬───────────┬───────────┬────────────┐
变长字段长度 空值位图 记录头信息 sensor_id co humidity
(记录所有列) (5字节) (TLM0100) (0.5010463) (35.047629)
└──────────────┴─────────┴───────────┴───────────┴───────────┴────────────┘
2. InfluxDB的存储方式
在磁盘上,InfluxDB 绝不会像 SQL 数据库那样按行连续存储。它的设计目标是极致的写入压缩和按时间范围的快速扫描
①逻辑存储
InfluxDB 会把你的数据解析成一张逻辑表:
| measurement | sensor_id | field | value | time |
|---|---|---|---|---|
| airSensors | TLM0100 | co | 0.5010463 | 1744851701 (2025-04-17 07:41:41) |
| airSensors | TLM0100 | humidity | 35.047629 | 1744851701 |
| airSensors | TLM0100 | temperature | 71.986109 | 1744851701 |
| airSensors | TLM0101 | co | 0.4996955 | 1744854451 (2025-04-17 08:27:31) |
| ... | ... | ... | ... | ... |
它既不是 MySQL 的二维表,也不是简单的 Map<SeriesKey, values>,而是一个三层嵌套结构:
Bucket (数据库)
│
└─ Measurement (类似于表,但无固定 Schema)
│
└─ Series (由 Tag 组合唯一确定的数据流)
│
└─ Field (每个 Field 是独立的时间序列)
│
└─ (time, value) 数据点
这俩条数据格式就是:
{
"bucket": "my_bucket",
"measurement": "airSensors",
"series": {
"sensor_id=TLM0100": {
"tags": {"sensor_id": "TLM0100"},
"fields": {
"co": [
{"time": 1744851701, "value": 0.5010463}
],
"humidity": [
{"time": 1744851701, "value": 35.047629}
],
"temperature": [
{"time": 1744851701, "value": 71.986109}
]
}
},
"sensor_id=TLM0101": {
"tags": {"sensor_id": "TLM0101"},
"fields": {
"co": [
{"time": 1744854451, "value": 0.4996955}
],
"humidity": [
{"time": 1744854451, "value": 35.018285}
],
"temperature": [
{"time": 1744854451, "value": 71.971565}
]
}
}
}
}
②物理存储( TSM 引擎)
第一步:数据分片(Sharding / Partitioning)
会将这两行行数据拆解,根据 Measurement + Tag Set 生成一个唯一的 Series Key(序列键),拆分成2个Series Key,会生成两条独立的时间线
SeriesKey 1:airSensors,sensor_id=TLM0100
SeriesKey 2:airSensors,sensor_id=TLM0101
第二步:列式分离(Field 独立存储)
这是最关键的一点。同一个 Series 里的 co、humidity、temperature 在物理上是分开存放的,可视化结构:
[ InfluxDB 存储索引 —— Series Key 索引 ]
│
├─ "airSensors,sensor_id=TLM0100" ◄── 这只是一个轻量级的"目录入口"
│ │
│ ├─ time 列文件 ──── [1744851701]
│ ├─ co 列文件 ────── [0.5010463] ◄── 独立压缩块
│ ├─ humidity 列文件 [35.047629] ◄── 独立压缩块
│ └─ temperature 列文件 [71.986109] ◄── 独立压缩块
│
└─ "airSensors,sensor_id=TLM0101"
│
├─ time 列文件 ──── [1744854451]
├─ co 列文件 ────── [0.4996955]
├─ humidity 列文件 [35.018285]
└─ temperature 列文件 [71.971565]
第三步:压缩(Compression)
-
时间戳压缩:InfluxDB 存储的是时间戳的差值。例如只记录
+0、+10s,而不是完整的 10 位数字。 -
浮点数压缩:使用 Gorilla 编码。对于像
71.986109和71.971565这种变化很小的温度值,它只存储前后两个值的 XOR 异或结果。因为数值相近,异或后会出现大量前导零,压缩比极高。
有点抽象,在磁盘上,你的两条数据被拆解为 6 个独立的数据流(2 个设备 × 3 个指标):
| 存储块标识 | 内容简述 |
|---|---|
| Shard 1 / TSM File | TLM0100 的时间戳列表 + co 压缩值 |
TLM0100 的时间戳列表 + humidity 压缩值 |
|
TLM0100 的时间戳列表 + temperature 压缩值 |
|
| Shard 1 / TSM File | TLM0101 的时间戳列表 + co 压缩值 |
TLM0101 的时间戳列表 + humidity 压缩值 |
|
TLM0101 的时间戳列表 + temperature 压缩值 |
上面从TSM文件层面去理解
TSM File
这里再说一下上面的TSM 文件,每个TSM文件2G大小,内部按Series Key + Field分组,同一个Field的数据点(时间戳+值)连续存放在一起,形成一个压缩块(Block)
那么磁盘上的TSM文件就是:
磁盘上的 TSM 文件(一个物理文件)
┌─────────────────────────────────────────────────────────────┐
│ Block: Series1, Field=co [时间戳序列] [值序列] │
├─────────────────────────────────────────────────────────────┤
│ Block: Series1, Field=humidity [时间戳序列] [值序列] │
├─────────────────────────────────────────────────────────────┤
│ Block: Series2, Field=co [时间戳序列] [值序列] │
├─────────────────────────────────────────────────────────────┤
│ Block: Series1, Field=temperature [时间戳序列] [值序列] │
└─────────────────────────────────────────────────────────────┘
简而言之:
-
同一个 Field 的数据在磁盘上物理连续(利于扫描)
-
同一个 Series (TLM0100) 的不同 Field 确实分开存储
-
每个 Field 有自己的时间戳列表 + 值列表
-
这些 Block 在同一个 TSM 文件中(涉及到Shard分片,后面文章再说)
因此,当你只查询
SELECT temperature FROM airSensors时,InfluxDB 完全不会去读取co和humidity的数据块,这是它比传统关系型数据库快得多的原因
这也是为什么 InfluxDB 查询单 Field 比 MySQL 快——因为不需要读取其他 Field 的数据
3.🔴 误区,重点!!!
前面了解了两种数据库的存储方式,本质上区别是很大的,但是依然有人会拿InfluxDB当成MySQL去使用,很大程度上是因为InfluxDB Studio将数据展示成MySQL的格式
| id | sensor_id | co | humidity | temperature | time |
|---|---|---|---|---|---|
| 1 | TLM0100 | 0.5010463 | 35.047629 | 71.986109 | 2025-04-17 07:41:41 |
| 2 | TLM0101 | 0.4996955 | 35.018285 | 71.971565 | 2025-04-17 08:27:31 |
底层实际上进行了列式存储和即时Join,
InfluxDB 引擎在后台做了一个非常消耗算力的动作:
Pivot(透视/行列转换) +Merge(按时间戳对齐合并)
它相当于在执行逻辑 SQL:
-- 实际上是把竖着的 N 条数据硬拼成一行
SELECT *
FROM
(SELECT time, co FROM field_1)
JOIN
(SELECT time, humidity FROM field_2) USING(time)
JOIN
(SELECT time, temperature FROM field_3) USING(time)
所以很多新手容易将InfluxDB当成MySQL,目的是为了降低学习负担,但这恰恰是理解 InfluxDB 最大的"甜蜜陷阱"
还是以上面例子来说明
场景:假设一个工厂的空气监控系统,有 5000 个传感器
-
传感器数量:5000 个
-
上报频率:每 10 秒一次
-
数据字段:设备ID、设备SN、温度、湿度、CO 浓度、项目ID
-
每天数据量:5000 × 8640 × 4 = 1.296 亿个数据点
①误区1:把 UI 表格当成真实存储
❌ 想看一下数据,查询所有数据,Select * 卡死
SELECT * FROM airSensors
翻车:查询跑了 30 秒没返回,浏览器卡死,后台数据库 CPU 飙到 100%,其他正常写入超时排队,数据开始丢失
实际:UI 实时把三个独立的列文件(temperature、humidity、co)按时间戳拼出来的。SELECT * 意味着要同时打开三个文件流,读取 1000 个传感器 × 86400 秒 × 3 个字段的数据点,然后在内存里做归并连接
✅ 查询永远带上时间范围
SELECT * FROM airSensors WHERE time > now() - 5m LIMIT 100
②误区2:把高基数字段当Tag
把 device_sn 设成 Tag,因为它"唯一标识设备"
翻车:工厂新增了 2000 个传感器,现在有 7000 个设备,后面如果增加了batch_id(生产批次号)做 Tag:
工厂每天 50 个批次,一个月 1500 个批次:
-
7000 个设备 × 1500 个批次 × 5 种类型 = 5250 万个 Series
真相:高基数 Tag 的灾难是指数级的。device_sn 作为 Tag 打开了一扇门,后续每个新 Tag 都会乘以这个基数
Series数量怎么算?
一般会觉得 device_id、device_sn、project_id 都可能是查询条件,于是全部设成 Tag
| Tag 字段 | 唯一值数量 | 关系分析 |
|---|---|---|
device_sn |
5000 个 | 全球唯一,设备的"身份证号" |
project_id |
10 个 | 项目标识 |
device_id |
5000 × 10 = 50,000 个 | 每个项目内的设备编码 |
实际 Series 数 = 不同 Tag 组合的数量
= 5000 (device_sn) × 10 (project_id)
= 50,000 个 Series
注意:device_id 虽然唯一值有 50,000 个,但它和 (device_sn, project_id) 组合是一一对应的,
所以不会额外增加 Series 数量。
Series 数量 = 实际写入的 Tag 键值对组合数(去重)
而不是:
❌ Tag1 唯一值数量 × Tag2 唯一值数量 × ... × TagN 唯一值数量(这是理论最大值)
判断标准:是否该用 Tag?
问自己三个问题:
-
这个字段是否经常用于 GROUP BY?(是 → Tag)
-
这个字段的唯一值数量是否 < 10万?(是 → Tag)
-
这个字段是连续数值吗?(是 → 绝对不做 Tag)
只有三个 YES 才适合做 Tag(或者至少满足前两个)
③误区3:无限制的数据保留与降采样缺失
❌ 所有原始数据永久保留,从来不删除
翻车:5000 个传感器,每秒采集一次,一天产生 5000 × 86400 = 4.32 亿个数据点。一个月 130 亿点,三个月 390 亿点。任何 GROUP BY 或范围查询都会触发大量磁盘 I/O,备份恢复基本不可能,磁盘很快写满导致写入失败
✅ 设置保留策略 + 降采样
-- 原始数据保留 7 天
CREATE RETENTION POLICY raw_7d ON airMonitor DURATION 7d REPLICATION 1
-- 1 分钟聚合数据保留 3 个月
CREATE RETENTION POLICY minute_3m ON airMonitor DURATION 90d REPLICATION 1
-- 1 小时聚合数据保留 2 年
CREATE RETENTION POLICY hour_2y ON airMonitor DURATION 730d REPLICATION 1
-- 降采样任务
CREATE CONTINUOUS QUERY cq_minute ON airMonitor
BEGIN
SELECT mean(temperature) AS temperature,
mean(humidity) AS humidity,
mean(co) AS co
INTO airMonitor.minute_3m
FROM airMonitor.raw_7d
GROUP BY time(1m), sensor_id
END
✅ 查询时按需选择数据精度
-- 趋势分析用降采样数据,速度快 100 倍
SELECT * FROM hour_2y
WHERE time > now() - 30d
-- 故障排查用原始数据,只查最近 1 小时
SELECT * FROM raw_7d
WHERE time > now() - 1h AND sensor_id = 'TLM0100'
④误区4:用 MySQL 查询习惯写 InfluxQL
(1)对 Field 字段使用 WHERE 过滤
❌ 错误写法:直接把 Field 字段(温度、湿度等数值列)放在 WHERE 条件中
查询温度超过 85 度的设备,写下:
SELECT * FROM airSensors WHERE temperature > 85
翻车:查询跑了 12 分钟,扫描了全部 1.7 亿个温度数据点,内存占用飙到 22GB,告警系统崩溃
真相:temperature是Field,没有索引,WHERE temperature > 85意味着全表扫描每一个温度值
因此在查询的时候最少需要锁死时间范围或者Tag
-- 必须先锁死时间范围
SELECT * FROM airSensors
WHERE time > now() - 1h
AND temperature > 85
-- 如果有正确设计(device_id 是 Tag),可以这样
SELECT * FROM airSensors
WHERE device_id = 'D-0001'
AND time > now() - 1h
AND temperature > 85
(2)修改或删除数据
InfluxDB不支持修改数据,只能追加,且支持按照时间范围删除数据,不支持单条删除
❌ 错误做法:想修改某个传感器的错误数值
-- MySQL 习惯了,想修改单条记录
UPDATE airSensors SET temperature = 23.5
WHERE sensor_id = 'TLM0100' AND time = '2024-01-15 10:30:00'
翻车:UPDATE 直接报错:unsupported statement
真相:InfluxDB 不支持修改数据(时序数据是追加写入的),数据一旦写入,就是不可变的(immutable)
✅ 正确做法:覆盖写入
覆盖写入(写入相同时间戳的数据会覆盖)
-- 先查询错误数据的时间戳:2024-01-15T10:30:00Z
-- 重新写入正确数据,使用相同的时间戳
airSensors,sensor_id=TLM0100 temperature=23.5 1705314600000000000
-- 旧数据会被覆盖
❌ 错误做法:删除某个时间段的数据
磁盘使用率 88%,你决定删除 30 天前的数据:
DELETE FROM airSensors WHERE time < '2026-03-20'
翻车:删除操作执行3小时候后,磁盘从88%->92%
真相:InfluxDB 的 DELETE 是写墓碑,不是真删除。墓碑本身占用空间,越删越满,不支持单条删除,只能按时间范围删除
✅设置Retention Poilcy,数据30天后,自动删除
CREATE RETENTION POLICY "30_days" ON "mydb" DURATION 30d DEFAULT
(3)补充:常见 MySQL 习惯导致的坑
坑1:忘记写时间范围
-- MySQL 习惯:数据量小,全表扫描也能接受
SELECT COUNT(*) FROM airSensors WHERE temperature > 80
-- InfluxDB:扫描 100 亿条数据,直接卡死
-- ✅ 必须加时间范围
SELECT COUNT(*) FROM airSensors
WHERE time > now() - 1h AND temperature > 80
坑2:对 Tag 使用函数
-- ❌ 错误:对 Tag 用函数,索引失效
SELECT * FROM airSensors WHERE LOWER(sensor_id) = 'TLM0100'
-- ✅ 正确:直接匹配,走索引
SELECT * FROM airSensors WHERE sensor_id = 'TLM0100'
坑3:期待自增 ID 或事务
-- MySQL:有自增主键
INSERT INTO airSensors (id, sensor_id, temperature) VALUES (1, 'S1001', 23.5)
-- InfluxDB:没有自增ID,没有事务
-- ✅ 时间戳 + Tag 组合就是唯一标识
坑4:用 LIMIT 做分页而不带时间条件
-- ❌ 错误:大 offset 扫描大量数据
SELECT * FROM airSensors LIMIT 10000, 1000
-- ✅ 正确:游标分页(基于时间)
SELECT * FROM airSensors
WHERE time > '2024-01-15T10:00:00Z'
LIMIT 1000
核心差异总结
|
维度 |
MySQL(InnoDB 行存) | InfluxDB(列存时序) |
|---|---|---|
| 最小数据单元 | 行(Row) | 点(Point) |
| 索引机制 | 自主建索引 | Tag 自动索引,Field 无索引 |
| 存储结构 | 按行连续存放 | 按列分离压缩 |
| 删除行为 | 立即释放空间 | 墓碑标记,延迟回收 |
| 擅长操作 | 事务、JOIN、点查 | 时间范围扫描、聚合、降采样 |
| 不适合 | 海量时序数据 | 事务、关联查询、频繁更新 |
写的有点长了,哈哈哈,本来打算在这片文章写SpringBoot集成InfluxDB2.x,但是相关文章太过潦草了,都是基于Flux,还是重新写相关的工具类吧,下一篇再写,完结,散花……
更多推荐





所有评论(0)