4. 数据存储层部署:时序数据库(InfluxDB)安装与配置、关系型数据库(PostgreSQL)设计、数据分片与备份策略
各位同事,这一章我们聊聊数据存储层。说白了,就是风场那些海量数据到底该往哪儿放、怎么放、怎么保证不丢。
我在多个风场项目里摸爬滚打过后,最大的体会是:存储层设计得好,后面运维能省一半的力气。你想想看,一个风场几百台风机,每台风机几十个测点,每秒都在产生数据。如果存储层没规划好,三个月后查询就慢得像蜗牛爬。
核心思路:时序数据(风速、功率、振动)走 InfluxDB,结构化业务数据(设备台账、运维记录、用户权限)走 PostgreSQL。两者各司其职,互不干扰。
4.1 时序数据库 InfluxDB 安装与配置
InfluxDB 这东西,说白了就是为时序数据量身定做的。我最早用的时候还是 1.x 版本,现在 2.x 已经非常成熟了。
4.1.1 安装步骤
我个人习惯用 Docker 部署,干净利落,升级也方便。当然,生产环境建议用二进制包安装,性能更好。
# Docker 方式(推荐开发测试用)
docker run -d \
--name influxdb \
-p 8086:8086 \
-v /data/influxdb:/var/lib/influxdb2 \
-v /data/influxdb-config:/etc/influxdb2 \
influxdb:2.7
# 二进制方式(生产环境推荐)
wget https://dl.influxdata.com/influxdb/releases/influxdb2-2.7.0-linux-amd64.tar.gz
tar xzf influxdb2-2.7.0-linux-amd64.tar.gz
cd influxdb2-2.7.0-linux-amd64
./influxd --reporting-disabled &
小技巧:启动时加上 --reporting-disabled 参数,可以关闭匿名数据上报。我在客户现场部署时,安全审计要求必须关掉这个。
4.1.2 初始化配置
安装完成后,需要做初始化。这里我踩过一个坑——bucket 的 retention policy 一定要提前想好。
# 通过 CLI 初始化
influx setup \
--username admin \
--password YourStrongPassword \
--org windfarm \
--bucket wind_turbine_data \
--retention 90d \
--force
# 创建完成后,生成 API Token
influx auth create \
--org windfarm \
--all-access
嗯,这里要注意:原始采样数据我建议保留 90 天。为什么?因为风机的振动分析、功率曲线诊断,通常需要回溯最近三个月的数据。超过三个月的,可以降采样后存到另一个 bucket 里。
4.1.3 关键配置参数
| 参数 | 推荐值 | 说明 |
|---|---|---|
| storage-cache-max-memory-size | 512m | 缓存最大内存,写密集场景可以调大 |
| storage-wal-max-write-size | 10m | WAL 写入阈值,避免频繁刷盘 |
| query-concurrency | 20 | 并发查询数,根据 CPU 核心数调整 |
| storage-series-id-set-cache-size | 100 | 序列缓存,tag 维度多时需要加大 |
我曾经在一个 200 台风机的项目里,默认配置下查询经常超时。后来把 query-concurrency 从 10 调到 20,再把 storage-cache-max-memory-size 调到 1G,问题就解决了。
4.2 关系型数据库 PostgreSQL 设计
PostgreSQL 这边,主要存的是「不随时间变化」或者「变化很慢」的数据。比如风机台账、备件信息、运维工单、用户权限。
4.2.1 核心表结构设计
我一般会设计这么几张核心表:
-- 风机基础信息表
CREATE TABLE wind_turbines (
turbine_id SERIAL PRIMARY KEY,
turbine_code VARCHAR(32) UNIQUE NOT NULL, -- 风机编码,如 WT-001
model VARCHAR(64), -- 机型
rated_power DECIMAL(10,2), -- 额定功率(MW)
longitude DECIMAL(10,6), -- 经度
latitude DECIMAL(10,6), -- 纬度
commission_date DATE, -- 并网日期
status VARCHAR(16) DEFAULT 'online', -- 状态
created_at TIMESTAMP DEFAULT NOW()
);
-- 测点配置表
CREATE TABLE measurement_points (
point_id SERIAL PRIMARY KEY,
turbine_id INT REFERENCES wind_turbines(turbine_id),
point_code VARCHAR(64) NOT NULL, -- 测点编码,如 WT-001-WS
point_name VARCHAR(128), -- 测点名称,如 风速
unit VARCHAR(16), -- 单位,如 m/s
data_type VARCHAR(16), -- 数据类型,float/int/bool
upper_limit DECIMAL(10,2), -- 报警上限
lower_limit DECIMAL(10,2), -- 报警下限
is_active BOOLEAN DEFAULT TRUE
);
-- 运维工单表
CREATE TABLE maintenance_orders (
order_id SERIAL PRIMARY KEY,
turbine_id INT REFERENCES wind_turbines(turbine_id),
order_type VARCHAR(32), -- 工单类型:巡检/维修/保养
description TEXT,
assigned_to VARCHAR(64),
priority INT DEFAULT 3, -- 优先级 1-5
status VARCHAR(16) DEFAULT 'pending', -- pending/in_progress/completed
created_at TIMESTAMP DEFAULT NOW(),
completed_at TIMESTAMP
);
注意:风机编码 turbine_code 一定要用唯一约束。我见过一个项目,因为编码重复导致数据关联全部错乱,最后花了三天才把数据清洗干净。
4.2.2 索引设计策略
查询性能的关键在索引。我的经验是:
- 外键字段必须建索引:比如
turbine_id,因为 JOIN 操作频繁 - 时间字段建索引:比如
created_at,按时间范围查询很常见 - 状态字段考虑部分索引:比如只对
status = 'pending'的工单建索引
CREATE INDEX idx_turbine_status ON wind_turbines(status);
CREATE INDEX idx_points_turbine ON measurement_points(turbine_id);
CREATE INDEX idx_orders_status ON maintenance_orders(status) WHERE status = 'pending';
4.3 数据分片与备份策略
这部分是真正的「避坑指南」。我见过太多风场因为没做好分片和备份,数据丢了之后追悔莫及。
4.3.1 InfluxDB 数据分片
InfluxDB 自带分片机制,按时间自动切分。但默认的 shard duration 不一定适合你的场景。
| 数据写入频率 | 推荐 Shard Duration | 说明 |
|---|---|---|
| 高频率(每秒 > 10000 点) | 1h | 减少单个 shard 数据量,提高写入性能 |
| 中频率(每秒 1000-10000 点) | 6h | 平衡写入和查询性能 |
| 低频率(每秒 < 1000 点) | 24h | 减少 shard 数量,简化管理 |
# 创建 bucket 时指定 shard duration
influx bucket create \
--name wind_turbine_data \
--org windfarm \
--retention 90d \
--shard-group-duration 6h
为什么会这样?你想想看,如果每秒写入 5 万个数据点,shard 设成 24 小时,一个 shard 里就有 4 亿多条记录。查询时扫描这么大一个 shard,不慢才怪。
4.3.2 PostgreSQL 分表策略
PostgreSQL 这边,我建议按时间分表。比如运维工单,按月份分表:
-- 创建主表
CREATE TABLE maintenance_orders (
order_id SERIAL,
turbine_id INT,
created_at TIMESTAMP DEFAULT NOW()
) PARTITION BY RANGE (created_at);
-- 创建分区
CREATE TABLE maintenance_orders_2024_01 PARTITION OF maintenance_orders
FOR VALUES FROM ('2024-01-01') TO ('2024-02-01');
CREATE TABLE maintenance_orders_2024_02 PARTITION OF maintenance_orders
FOR VALUES FROM ('2024-02-01') TO ('2024-03-01');
-- 以此类推...
经验之谈:分区表记得定期创建新分区。我写了个 cron 脚本,每月 1 号自动创建下个月的分区。这样就不会出现数据写不进去的尴尬。
4.3.3 备份策略
备份这件事,我吃过亏。以前有个项目,硬盘坏了才发现备份文件也坏了——因为备份和源数据在同一个磁盘阵列上。
我的建议是:
- InfluxDB 备份:用
influx backup命令,备份到独立的 NAS 或对象存储 - PostgreSQL 备份:用
pg_dump做逻辑备份,同时用pg_basebackup做物理备份 - 备份频率:全量备份每周一次,增量备份每天一次
- 异地容灾:至少保留一份备份在异地机房或云上
# InfluxDB 全量备份
influx backup /backup/influxdb/$(date +%Y%m%d) --org windfarm
# PostgreSQL 逻辑备份
pg_dump -U postgres -d windfarm_db -F c -f /backup/pgsql/$(date +%Y%m%d).dump
# 恢复命令
influx restore /backup/influxdb/20240101 --org windfarm
pg_restore -U postgres -d windfarm_db /backup/pgsql/20240101.dump
警告:备份一定要定期做恢复演练!我见过太多人备份了三年,恢复时才发现文件损坏。每个月抽一天,在测试环境做一次完整恢复,这钱花得值。
4.4 本章知识体系
下面这张图,是我对数据存储层整体架构的理解。你可以把它贴在工位上,部署时对照着来。
这张图把整个数据存储层的脉络理清楚了。从上到下,数据从风机流过来,经过接入层清洗,然后兵分两路——时序数据进 InfluxDB,结构化数据进 PostgreSQL。最下面是备份层,兜底保障。
好了,数据存储层就聊到这儿。记住一句话:存储设计是地基,地基不稳,上层再漂亮的系统也是空中楼阁。