3、材料数据库构建:关系型与非关系型数据库选型、数据建模、数据版本控制

做材料AI项目,最头疼的往往不是模型选型,而是数据。

我见过太多团队,算法工程师调参调了三个月,最后发现是数据库里存的数据格式不对,或者版本搞混了。说白了,数据库就是AI项目的“地基”。地基没打好,上面盖多漂亮的模型都得塌。

这一章,咱们就聊聊材料数据库怎么建。我会结合自己踩过的坑,把选型、建模、版本控制这三件事讲透。

3.1 关系型 vs 非关系型:到底选哪个?

这个问题,几乎每个新项目启动时都会被问到。我的回答很简单:看你的数据长什么样

3.1.1 关系型数据库(SQL)—— 结构化数据的“老黄牛”

关系型数据库,比如 PostgreSQL、MySQL,适合存储高度结构化的数据。材料科学里,典型的场景是:

  • 成分-工艺-性能表:合金成分(Fe: 0.8, C: 0.2)、热处理温度(850°C)、拉伸强度(1200 MPa)。每一行是一条记录,字段固定。
  • 实验元数据:实验日期、操作员、设备编号、样品批次。这些信息规规矩矩,用 SQL 管理非常顺手。

我个人习惯,只要数据能用二维表格描述清楚,优先选 PostgreSQL。为什么?

  • ACID 事务:多用户同时写入时,数据不会乱。我在项目中遇到过,两个实验员同时录入同一批样品的数据,如果没有事务控制,最后查出来数据对不上,排查了整整两天。
  • 强大的查询能力:想找“所有含 Cr 且 Cr 含量在 0.5%~1.0% 之间的合金”,一条 SQL 就搞定。
  • 生态成熟:Python 的 SQLAlchemy、Pandas 都能无缝对接。

核心建议:如果你的材料数据是“表格状”的,字段数量固定,且需要频繁做范围查询、聚合统计,别犹豫,上 PostgreSQL。

3.1.2 非关系型数据库(NoSQL)—— 灵活数据的“瑞士军刀”

材料科学里,不是所有数据都那么规矩。比如:

  • 高分辨率显微图像:一张 SEM 图像可能有 4096x4096 像素,还附带几十个元数据标签。
  • 光谱数据:XRD 图谱、拉曼光谱,每个文件长度不一,结构不同。
  • 计算模拟输出:DFT 计算的结果,包含晶格参数、能带结构、态密度……嵌套层级很深。

这些数据用关系型数据库存,要么得拆成几十张表,要么得把 JSON 塞进一个字段里——嗯,后者其实就是在“假装用 SQL 做 NoSQL 的事”。

我建议,遇到以下情况,考虑 MongoDB 或 Cassandra:

  • 数据模式不固定:今天存了 5 个字段,明天可能变成 8 个。
  • 数据嵌套深:比如一个“实验”文档,下面包含“样品”、“设备参数”、“原始数据”、“分析结果”等多个子文档。
  • 读写吞吐量极高:比如实时采集的传感器数据流。

我的经验:在某个电池材料项目中,我们需要存储不同实验室上传的充放电曲线。每个实验室的格式都不一样,有的带温度,有的带压力,有的只有电压和容量。用 MongoDB 的文档模型,每个曲线存成一个文档,字段按需添加。如果当时硬要用 SQL,光设计表结构就得花两周。

3.1.3 选型对比表

维度 关系型(PostgreSQL) 非关系型(MongoDB)
数据结构 高度结构化,字段固定 半结构化/非结构化,灵活
查询方式 SQL,支持复杂 JOIN 文档查询,聚合管道
事务支持 强 ACID 有限支持(MongoDB 4.0+)
扩展性 垂直扩展为主 水平扩展原生支持
典型场景 成分-工艺-性能表、实验元数据 图像、光谱、模拟输出

3.2 数据建模:别让数据变成“垃圾堆”

选好了数据库,下一步就是建模。这一步做不好,后面所有查询都会变成噩梦。

3.2.1 关系型建模:范式化与反范式化

关系型数据库建模,核心是“范式化”——把数据拆成多个表,减少冗余。比如:

  • 材料表:material_id, composition, processing
  • 性能表:property_id, material_id, test_type, value
  • 设备表:equipment_id, name, calibration_date

这样做的好处是数据一致性高。但坏处也很明显:查询时 JOIN 太多,性能会下降。

我在项目中遇到过,一个材料性能查询需要 JOIN 7 张表,结果跑了 30 秒才出来。后来我们把常用的字段冗余到一张“宽表”里,查询时间降到了 200 毫秒。

避坑指南:我曾经在一个项目中,把所有实验数据都塞进一张“万能表”,字段超过 100 个。结果数据量一上来,索引都建不了,查询慢得像蜗牛。记住:范式化保证一致性,反范式化保证性能。两者要平衡。

3.2.2 非关系型建模:文档设计原则

MongoDB 的文档模型,核心是“按查询设计”。你想想看,用户最常查什么?

  • 如果用户常查“某个材料的所有实验数据”,那就把实验数据嵌套在材料文档里。
  • 如果用户常查“所有做过某种实验的材料”,那就把材料 ID 作为数组,存在实验文档里。

举个例子,一个典型的材料实验文档:

{
  "_id": "exp_001",
  "material": {
    "name": "Al6061",
    "composition": {"Al": 0.975, "Mg": 0.01, "Si": 0.006}
  },
  "test": {
    "type": "tensile",
    "equipment": "Instron_5982",
    "date": "2024-03-15"
  },
  "results": {
    "yield_strength": 276,
    "tensile_strength": 310,
    "elongation": 12.0
  },
  "raw_data": {
    "file_path": "/data/raw/tensile_001.csv",
    "sampling_rate": 100
  }
}

这个文档把材料信息、实验条件、结果、原始数据路径都放在一起。查询时一次读取,不需要 JOIN。但要注意:嵌套不能太深,否则更新某个子字段会很麻烦。

我的习惯:嵌套层级控制在 3 层以内。超过 3 层,我会考虑拆成两个集合,用引用(reference)关联。

3.3 数据版本控制:别让“昨天还能用”变成“今天跑不通”

材料科学的数据,经常要更新。今天修正了一个成分值,明天补充了一个性能数据,后天发现原始文件被覆盖了……如果没有版本控制,AI 模型训练时用的数据到底是哪个版本?

这个问题,我吃过亏。有一次,我们训练了一个预测模型,效果很好。结果两周后,发现数据库里的数据被人改过,模型重新训练后性能下降了一大截。排查了三天,才发现是数据版本不一致。

3.3.1 数据库级别的版本控制

最简单的方法:在每张表或每个文档里加一个 version 字段。

关系型数据库示例:

CREATE TABLE material_properties (
    id SERIAL PRIMARY KEY,
    material_id INT NOT NULL,
    property_name VARCHAR(50),
    property_value FLOAT,
    version INT DEFAULT 1,
    created_at TIMESTAMP DEFAULT NOW(),
    updated_at TIMESTAMP DEFAULT NOW(),
    UNIQUE (material_id, property_name, version)
);

每次更新时,不是直接修改原记录,而是插入一条新记录,version 加 1。查询时默认取最新版本:

SELECT * FROM material_properties
WHERE material_id = 123 AND property_name = 'tensile_strength'
ORDER BY version DESC
LIMIT 1;

MongoDB 里类似,可以用一个数组存储历史版本:

{
  "_id": "mat_123",
  "name": "Al6061",
  "versions": [
    {"version": 1, "composition": {"Al": 0.97}, "timestamp": "2024-01-01"},
    {"version": 2, "composition": {"Al": 0.975}, "timestamp": "2024-03-01"}
  ],
  "current_version": 2
}

3.3.2 文件级别的版本控制

对于原始数据文件(图像、光谱、模拟输出),我建议用 Git LFS(Large File Storage)或者 DVC(Data Version Control)。

DVC 是我目前最常用的工具。它和 Git 配合,只跟踪文件的元数据(哈希值),实际文件存在云存储或 NAS 上。这样既保留了版本历史,又不会让 Git 仓库变得臃肿。

基本用法:

# 初始化 DVC
dvc init

# 添加数据文件
dvc add data/raw/sem_image_001.tif

# 提交到 Git
git add data/raw/sem_image_001.tif.dvc
git commit -m "add SEM image v1"

# 切换到另一个版本
git checkout <commit_hash>
dvc checkout

核心原则数据不可变,变更即新版本。 永远不要覆盖原始数据。每次修改,都生成一个新版本,并记录谁、什么时候、为什么改的。

3.4 知识体系总览

下面这张图,概括了本章的核心逻辑:

材料数据库构建核心逻辑 实验数据 模拟数据 文献数据 数据库选型决策 结构化数据 → PostgreSQL | 半结构化/嵌套数据 → MongoDB 关系型建模 范式化 + 反范式化平衡 非关系型建模 按查询设计,嵌套 ≤ 3 层 数据版本控制:version 字段 / DVC / Git LFS

3.5 小结

数据库构建这件事,说难不难,说简单也不简单。核心就三点:

  • 选型看数据形态:规规矩矩的表格用 PostgreSQL,灵活嵌套的用 MongoDB。
  • 建模要平衡:别过度范式化,也别搞成“万能大表”。按查询场景设计。
  • 版本控制不能省:数据改了就是新版本,永远保留历史。这是 AI 项目可复现性的基础。

嗯,这些经验,都是我在一个个项目中摔打出来的。希望你能少走一些弯路。