前言:最初动笔,本想直接分享InfluxDB的实用使用技巧,帮大家避开实操中的坑、提升效率。但在查阅各类相关文章后,我发现很多内容都停留在表面,不仅没能讲透核心逻辑,还存在不少常见误区——最典型的就是大家习惯性地把关系数据库(比如MySQL)的使用思维,套用到InfluxDB这类时序数据库上,忽略了两者的本质区别,导致写出来的配置、执行的查询要么效率低下,要么根本不符合时序数据的存储规律

目录

一、InfluxDB写入原理

1. InfluxDB 1.x 写入流程 (TSM Engine)

2. InfluxDB 2.x 写入流程 (TSM Engine + OSS)

3. InfluxDB 3.x 写入流程 (IOx/Arrow Engine)

二、InfluxDB数据导入

1. 创建Token

2. 配置InfluxDB

3. 写入数据

① Line Prtocol 行协议

② CSV 

③ Influx CLI 

三、InfluxDB核心概念

1. Bucket(桶)

2. Measurement(度量)

3. Tag(标签)

4. Field(字段)

5. TimeStamp(时间戳)

6. Point(数据点)

7. Series(时间线)

❓Series 为什么超级重要?

四、与关系型的数据库MySQL的区别

1. MySQL的存储方式

①逻辑存储

②物理存储(InnoDB 引擎)

2. InfluxDB的存储方式

①逻辑存储

②物理存储( TSM 引擎)

第一步:数据分片(Sharding / Partitioning)

第二步:列式分离(Field 独立存储)

第三步:压缩(Compression)

3.🔴 误区,重点!!!

①误区1:把 UI 表格当成真实存储

②误区2:把高基数字段当Tag

③误区3:无限制的数据保留与降采样缺失

④误区4:用 MySQL 查询习惯写 InfluxQL


        这其中,除了大家对“时序数据库”的核心逻辑理解不深,也和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 文件,经典时序数据库架构

流程步骤:

  1. 写入 WAL:数据先写入磁盘上的预写日志,保证崩溃不丢数据
  2. 写入内存 Cache:再写入内存缓存,对外返回写入成功,提升性能
  3. 后台异步刷盘:Cache 中的数据按时间 / 大小策略,后台写入 TSM 持久化文件
  4. 合并压缩:后台 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、FieldPoint、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 字符串 字段名(动态的) cohumiditytemperature
_value 多种类型 字段值(对应的数值) 0.501046335.04762971.986109
sensor_id 字符串 Tag列(自定义标签) TLM0100TLM0101TLM0102

可以看出有两种不一样的形式

🔴 重点:

InfluxDB 在导入 CSV 数据时,底层并非直接存储 CSV 格式,而是会按照 CSV 中的表头、注释(#datatype#group_measurement_field_valuetag 等)自动映射并转换为 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 为什么超级重要?

因为:

  1. InfluxDB 每一个 series 都会占用内存

  2. 索引、查询、分片、压缩、内存占用 全都以 series 为单位

  3. 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_iddevice_snproject_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?

问自己三个问题:

  1. 这个字段是否经常用于 GROUP BY?(是 → Tag)

  2. 这个字段的唯一值数量是否 < 10万?(是 → Tag)

  3. 这个字段是连续数值吗?(是 → 绝对不做 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,还是重新写相关的工具类吧,下一篇再写,完结,散花……

Logo

汇聚全球AI编程工具,助力开发者即刻编程。

更多推荐