第四章:数据库设计与建模
数据库设计这事儿,说实话,是智能制造平台的地基。地基没打好,后面盖再漂亮的楼都得塌。我见过太多项目,前期图省事随便搞搞表结构,结果数据量一上来,查询慢得像蜗牛,改又改不动,最后只能推倒重来。
今天咱们就聊聊,半导体智能制造平台里,数据库到底该怎么设计。
4.1 关系型数据库:PostgreSQL vs MySQL
先说说关系型数据库。PostgreSQL 和 MySQL,到底选哪个?
我个人习惯用 PostgreSQL。为什么?因为半导体制造的数据类型太杂了。你想想看,有设备参数、工艺配方、良率数据、批次信息……PostgreSQL 对 JSON、数组、自定义类型的支持,比 MySQL 强太多。
举个例子,设备参数里经常有「温度曲线」这种数据,是一串时间-温度对。PostgreSQL 可以直接用数组或者 JSONB 存,查询起来特别方便。MySQL 虽然也能存 JSON,但功能弱不少。
当然,MySQL 也有它的优势。部署简单,运维成本低,社区资源丰富。如果你的团队里 DBA 资源紧张,MySQL 可能更合适。
4.2 表结构设计实战
好,假设我们选了 PostgreSQL。那表该怎么设计?
我直接给出一套我在项目中用过的核心表结构,你们可以参考。
设备信息表(equipment)
CREATE TABLE equipment (
id SERIAL PRIMARY KEY,
equipment_id VARCHAR(50) UNIQUE NOT NULL, -- 设备编号
equipment_name VARCHAR(100) NOT NULL, -- 设备名称
equipment_type VARCHAR(50), -- 设备类型
location VARCHAR(100), -- 位置
status VARCHAR(20) DEFAULT 'idle', -- 状态
install_date DATE, -- 安装日期
last_maintenance TIMESTAMP, -- 上次维护时间
created_at TIMESTAMP DEFAULT NOW(),
updated_at TIMESTAMP DEFAULT NOW()
);
工艺配方表(recipe)
CREATE TABLE recipe (
id SERIAL PRIMARY KEY,
recipe_id VARCHAR(50) UNIQUE NOT NULL,
recipe_name VARCHAR(100) NOT NULL,
product_id VARCHAR(50), -- 关联产品
parameters JSONB, -- 工艺参数(JSON格式)
version INT DEFAULT 1, -- 版本号
is_active BOOLEAN DEFAULT TRUE,
created_by VARCHAR(50),
created_at TIMESTAMP DEFAULT NOW()
);
这里有个坑,我必须要说。参数用 JSONB 存,确实灵活,但查询性能会受影响。我的做法是:高频查询的字段单独拎出来建索引,低频查询的放 JSONB 里。
生产批次表(lot)
CREATE TABLE lot (
id SERIAL PRIMARY KEY,
lot_id VARCHAR(50) UNIQUE NOT NULL,
product_id VARCHAR(50) NOT NULL,
quantity INT NOT NULL,
current_step VARCHAR(50), -- 当前工序
status VARCHAR(20) DEFAULT 'in_progress',
start_time TIMESTAMP,
end_time TIMESTAMP,
equipment_id VARCHAR(50), -- 当前所在设备
recipe_id VARCHAR(50) -- 当前使用的配方
);
4.3 时序数据库选型:InfluxDB vs TimescaleDB
关系型数据库搞定了业务数据,但半导体制造里还有一类数据——时序数据。设备传感器每秒都在产生数据:温度、压力、流量、振动……这些数据的特点是:写入量大、查询范围广、很少更新。
用关系型数据库存时序数据?不是不行,但你会后悔的。我试过用 PostgreSQL 存传感器数据,一个月后单表就上亿条,查询慢得让人崩溃。
那选 InfluxDB 还是 TimescaleDB?
| 对比项 | InfluxDB | TimescaleDB |
|---|---|---|
| 存储引擎 | 自研 TSM 引擎 | 基于 PostgreSQL |
| 写入性能 | 极高(百万点/秒) | 高(十万级/秒) |
| 查询语法 | 类 SQL(Flux) | 标准 SQL |
| 与关系型数据联动 | 需要额外开发 | 天然支持 JOIN |
| 运维复杂度 | 中等 | 低(PG 生态) |
我个人建议:如果团队熟悉 SQL,选 TimescaleDB。它本质上是 PostgreSQL 的扩展,你可以直接用 SQL 查询时序数据,还能跟业务表做 JOIN。InfluxDB 虽然写入性能更强,但学习成本高,而且跟业务数据联动比较麻烦。
4.4 时序数据建模
时序数据库的建模思路跟关系型数据库不一样。核心原则是:按时间分区,按标签索引。
以 TimescaleDB 为例,传感器数据表可以这样设计:
CREATE TABLE sensor_data (
time TIMESTAMPTZ NOT NULL,
equipment_id VARCHAR(50) NOT NULL,
sensor_type VARCHAR(50) NOT NULL,
value DOUBLE PRECISION,
unit VARCHAR(20)
);
-- 转换为超表(按时间分区)
SELECT create_hypertable('sensor_data', 'time');
-- 建索引
CREATE INDEX idx_equipment_sensor
ON sensor_data (equipment_id, sensor_type, time DESC);
这里要注意:不要把 equipment_id 和 sensor_type 拆成多个表。时序数据库的设计理念是「宽表」,把所有传感器的数据放一张表里,通过标签来区分。这样查询时只需要扫描一个分区,效率极高。
4.5 数据字典与 ER 图
数据字典这东西,很多人觉得可有可无。但我告诉你,没有数据字典的项目,三个月后就是一团乱麻。
我习惯用 Markdown 写数据字典,放在项目的 docs 目录下,跟代码一起版本管理。格式大概这样:
## 设备信息表(equipment)
| 字段名 | 类型 | 说明 | 示例 |
|--------|------|------|------|
| equipment_id | VARCHAR(50) | 设备编号,主键 | EQ-001 |
| equipment_name | VARCHAR(100) | 设备名称 | 光刻机A |
| status | VARCHAR(20) | 状态:idle/running/down | running |
| parameters | JSONB | 设备参数 | {"temp": 25.0} |
至于 ER 图,我推荐用 PlantUML 画,纯文本,方便版本管理。下面是我用 SVG 画的一个核心 ER 图:
4.6 数据备份与恢复策略
备份这事儿,平时没人关心,出事了才追悔莫及。我见过一个工厂,数据库硬盘坏了,才发现备份脚本三个月前就失效了……那场面,真是欲哭无泪。
我的备份策略很简单,就三条:
- 全量备份 + 增量备份:每周日做全量备份,每天做增量备份。PostgreSQL 用 pg_dump,TimescaleDB 也一样。
- 异地备份:备份文件至少存两份,一份本地,一份远程(比如对象存储)。
- 定期演练:每个月至少做一次恢复演练,确保备份文件可用。
具体的备份脚本,我贴一个 PostgreSQL 的:
#!/bin/bash
# 全量备份脚本
BACKUP_DIR="/data/backup/postgres"
DB_NAME="smart_fab"
DATE=$(date +%Y%m%d)
pg_dump -h localhost -U admin -Fc $DB_NAME > $BACKUP_DIR/full_${DATE}.dump
# 删除30天前的备份
find $BACKUP_DIR -name "*.dump" -mtime +30 -delete
恢复命令也很简单:
pg_restore -h localhost -U admin -d smart_fab full_20240101.dump
对于 TimescaleDB,备份方式跟 PostgreSQL 完全一样,因为它本身就是 PG 的扩展。InfluxDB 的话,可以用它的 backup 命令,但恢复起来稍微麻烦点,建议用对象存储做快照。
好了,数据库设计这块就聊到这儿。记住一句话:设计时多花一小时,运维时少花一天。别图省事,该建的索引建上,该写的文档写好,该做的备份做全。这些都是基本功,但基本功往往决定了一个项目能走多远。