3. InfluxDB入门:安装配置、核心概念与基础CRUD操作

3.1 为什么选InfluxDB?

做电力数据存储,我第一个想到的就是InfluxDB。为什么?

你想想看,电力系统里全是时序数据——电压、电流、功率、频率,每秒都可能产生成千上万个采样点。传统的关系型数据库存这些数据,读写性能很快就扛不住了。InfluxDB就是专门为这种场景设计的。

我个人习惯把InfluxDB比作「为时间而生的数据库」。它把时间戳作为核心索引,写入和查询都围绕时间轴展开。这在电力数据回放场景下,简直是天作之合。

3.2 安装配置:三分钟跑起来

安装其实很简单。我一般在Linux服务器上部署,Windows和Mac也支持。

方式一:直接安装(Linux)

# Ubuntu/Debian
wget -q https://repos.influxdata.com/influxdata-archive_compat.key
echo '393e8779c89ac8d958f81f942f9ad7fb82a25e133faddaf92e15b16e6ac9ce4c influxdata-archive_compat.key' | sha256sum -c && cat influxdata-archive_compat.key | gpg --dearmor | sudo tee /etc/apt/trusted.gpg.d/influxdata-archive_compat.gpg > /dev/null
echo 'deb [signed-by=/etc/apt/trusted.gpg.d/influxdata-archive_compat.gpg] https://repos.influxdata.com/debian stable main' | sudo tee /etc/apt/sources.list.d/influxdata.list
sudo apt-get update && sudo apt-get install influxdb2

# 启动服务
sudo systemctl start influxdb
sudo systemctl enable influxdb

方式二:Docker部署(我推荐)

我在项目中经常用Docker,环境隔离,迁移方便。

docker run -d \
  --name influxdb \
  -p 8086:8086 \
  -v influxdb-data:/var/lib/influxdb2 \
  influxdb:2.7

启动后,浏览器访问 http://localhost:8086,设置用户名、密码、组织名和初始Bucket(相当于数据库)。

小提示: 初次配置时,记得把生成的API Token保存好。我一开始就吃过这个亏,Token丢了只能重置。

3.3 核心概念:Measurement、Tag、Field

这三个概念是InfluxDB的灵魂。搞懂了它们,你就掌握了80%的InfluxDB。

3.3.1 Measurement(测量)

说白了,Measurement就是一张「表」。但它比关系型数据库的表灵活得多——不需要预先定义字段结构。

在电力场景中,我通常这样设计:

  • power_meter — 电表数据
  • transformer_status — 变压器状态
  • line_current — 线路电流

3.3.2 Tag(标签)

Tag是被索引的字段。用来做筛选和分组。

Tag的特点:

  • 必须是字符串类型
  • 会被自动索引,查询快
  • 适合存「元数据」——设备ID、区域、类型等

举个例子:

power_meter,device_id=PM-001,region=华东,phase=A voltage=220.5 1700000000

这里的 device_idregionphase 就是Tag。

经验之谈: 我在项目中遇到过一个问题——把「电压值」误存成了Tag。结果查询时发现数据量暴增,索引膨胀得厉害。记住:只有需要筛选的元数据才放Tag,数值数据放Field

3.3.3 Field(字段)

Field就是实际的数据值。可以是浮点数、整数、字符串、布尔值。

Field不会被自动索引,所以查询时如果按Field过滤,性能会差一些。

power_meter,device_id=PM-001 voltage=220.5,current=5.2,power=1146.6 1700000000

这里的 voltagecurrentpower 就是Field。

3.3.4 时间戳(Timestamp)

每条数据都必须带时间戳。默认是纳秒精度。

嗯,这里要注意:InfluxDB的时间戳精度可以配置,但一旦写入就不能改了。我建议统一用纳秒,避免后续数据对齐出问题。

3.4 基础CRUD操作

InfluxDB使用类SQL的查询语言——Flux(2.x版本)或InfluxQL。我主要用Flux,更灵活。

3.4.1 写入数据(Create)

行协议格式:

<measurement>[,tag_key=tag_value...] field_key=field_value[,field_key=field_value...] [timestamp]

示例:写入一条电表数据

# 通过CLI写入
influx write \
  --bucket power_data \
  --precision s \
  "power_meter,device_id=PM-001,region=华东,phase=A voltage=220.5,current=5.2,power=1146.6 1700000000"

# 通过API写入
curl -X POST "http://localhost:8086/api/v2/write?bucket=power_data&precision=s" \
  --header "Authorization: Token YOUR_TOKEN" \
  --data-binary 'power_meter,device_id=PM-001,region=华东,phase=A voltage=220.5,current=5.2,power=1146.6 1700000000'
注意: 写入时如果Measurement不存在,InfluxDB会自动创建。但Tag的基数(唯一值数量)不能太高,否则索引会爆炸。我曾经见过有人把「毫秒级时间戳」当Tag用,结果数据库直接挂了。

3.4.2 查询数据(Read)

Flux查询示例:

from(bucket: "power_data")
  |> range(start: -1h)
  |> filter(fn: (r) => r._measurement == "power_meter" and r.device_id == "PM-001")
  |> filter(fn: (r) => r._field == "voltage" or r._field == "current")
  |> aggregateWindow(every: 1m, fn: mean)
  |> yield(name: "mean_values")

这段代码的意思是:从 power_data 桶中,查过去1小时内设备PM-001的电压和电流,按1分钟窗口取平均值。

InfluxQL查询(兼容模式):

SELECT mean("voltage") AS "avg_voltage"
FROM "power_data"."autogen"."power_meter"
WHERE "device_id" = 'PM-001'
  AND time >= now() - 1h
GROUP BY time(1m)

我个人习惯用Flux,虽然语法稍微复杂点,但处理复杂的时间窗口、数据变换时,Flux强大得多。

3.4.3 更新数据(Update)

InfluxDB不支持直接更新已写入的数据。这是时序数据库的特点——数据一旦写入,就是不可变的。

那如果写错了怎么办?

  • 方法一: 删除错误数据,重新写入
  • 方法二: 写入一条新数据覆盖(查询时取最新值)
# 删除指定时间范围内的数据
influx delete \
  --bucket power_data \
  --start 2024-01-01T00:00:00Z \
  --stop 2024-01-01T01:00:00Z \
  --predicate '_measurement="power_meter" AND device_id="PM-001"'
避坑指南: 我曾经在生产环境误写了一批数据,想用更新操作。结果发现InfluxDB根本没有UPDATE语句。后来只能写脚本批量删除再重写。所以写入前一定要校验数据质量。

3.4.4 删除数据(Delete)

删除操作要谨慎。InfluxDB的删除是按时间范围和标签谓词来做的。

# 通过API删除
curl -X POST "http://localhost:8086/api/v2/delete?bucket=power_data" \
  --header "Authorization: Token YOUR_TOKEN" \
  --header "Content-Type: application/json" \
  --data '{
    "start": "2024-01-01T00:00:00Z",
    "stop": "2024-01-02T00:00:00Z",
    "predicate": "_measurement=\"power_meter\" AND region=\"华东\""
  }'

注意:删除操作不能指定Field,只能按Measurement和Tag来删。这是InfluxDB的设计限制。

3.5 实战:电力数据写入与查询

最后,我分享一个实际项目中用到的模式。

场景: 变电站每台变压器每5秒上报一次三相电压、电流数据。

数据模型设计:

transformer_data,station_id=ST-001,transformer_id=TF-01,phase=A \
  voltage=110.2,current=256.3,temperature=45.6 1700000000

transformer_data,station_id=ST-001,transformer_id=TF-01,phase=B \
  voltage=109.8,current=254.1,temperature=44.9 1700000000

transformer_data,station_id=ST-001,transformer_id=TF-01,phase=C \
  voltage=110.5,current=257.8,temperature=45.2 1700000000

查询:最近10分钟三相电压平均值

from(bucket: "power_data")
  |> range(start: -10m)
  |> filter(fn: (r) => r._measurement == "transformer_data" 
      and r.station_id == "ST-001" 
      and r.transformer_id == "TF-01")
  |> filter(fn: (r) => r._field == "voltage")
  |> group(columns: ["phase"])
  |> mean()
  |> yield(name: "avg_voltage_by_phase")

你看,用Tag来区分相位,查询时按phase分组,就能轻松得到三相各自的平均值。这就是InfluxDB的设计哲学——把筛选条件放Tag,把数值放Field。

好了,这一章就到这里。下一章我们深入聊聊InfluxDB的存储引擎和保留策略,看看它是怎么做到「秒级写入百万点」的。