3. 医疗数据湖存储层设计
存储层是医疗数据湖仓的地基。它要同时承载三类差异极大的数据:影像与波形等大体量非结构化文件、检验检查等结构化明细、以及日志与设备流等半结构化记录。设计目标不是“选一个最便宜的桶”,而是在成本、吞吐、格式效率、分区可维护性、合规保留五个约束下取得平衡。本节按对象存储选型、文件格式、分区策略、生命周期管理、保留合规五条线索展开,并给出可落地的参数示例。
一、对象存储选型:先看访问模式,再看接口与合规
医疗湖仓的底座通常是对象存储(S3 兼容、OSS、COS、OBS 等),而非 HDFS。原因是对象存储具备近乎无限的扩展性、按量计费、多副本或纠删码持久性,并且能被 Spark、Flink、Trino、Hive 等计算引擎通过 S3A/OSS 连接器直接访问。选型时建议按以下维度打分:
- 访问模式:影像归档属于“写一次、极少读”,适合低频/归档层;而临床指标宽表被 BI 与科研反复扫描,需要标准层与高并发读。
- 一致性模型:现代对象存储已提供强一致读后写,这对“写入即查询”的湖仓入湖任务很关键,避免出现文件已提交但列表看不到的经典问题。
- 接口兼容:优先选择 S3 API 兼容,便于引擎无改造迁移;同时确认是否支持分段上传、多版本、对象锁(Object Lock)。
- 合规与地域:患者数据须落在境内指定地域,跨域复制需评估是否触发数据出境合规审查。
一个常见的分层做法是:标准存储放近 90 天热数据,低频存储放 90 天至 2 年温数据,归档/冷归档放 2 年以上历史影像与出院病案。分层不是简单改存储类别,而要配合生命周期规则自动迁移,否则人工搬运会迅速失控。
二、文件格式:Parquet、ORC、Avro 各司其职
三种格式并非互相替代,而是对应不同阶段:
| 格式 | 存储模型 | 典型用途 | 注意点 |
|---|---|---|---|
| Parquet | 列式,带页级统计与字典编码 | 湖仓明细表、宽表、BI 与即席分析 | 嵌套 schema 演进需谨慎;小文件会放大元数据开销 |
| ORC | 列式,含索引与谓词下推 | Hive 生态、需要强过滤的聚合表 | 与 Parquet 功能重叠,团队应统一一种为主 |
| Avro | 行式,schema 随文件写入 | Kafka 落地、CDC 缓冲、需保留完整变更记录 | 不适合大范围列扫描,通常作为入湖前的暂存层 |
推荐链路是:设备与 HIS 变更经 Kafka 以 Avro 落地到“原始层”,保留逐条变更与 schema;再由 Spark 清洗、去重、合并为 Parquet 写入“明细层”与“汇总层”。这样既保留了可追溯的原始记录,又让分析层获得列裁剪与压缩收益。Parquet 常用参数示例:
spark.sql.parquet.compression.codec = zstd
parquet.block.size = 134217728 # 128MB
parquet.page.size = 1048576 # 1MB
spark.sql.files.maxRecordsPerFile = 2000000
压缩建议用 zstd:相比 snappy 通常有更高压缩比,且解压速度可接受,适合医学文本与编码字段混合的场景。若表以数值型检验结果为主,可对比 zstd 与 gzip 的实际落盘大小后再定。
三、分区策略:按时间 + 业务维度,控制分区数量
分区决定扫描裁剪效率,也决定元数据压力。医疗数据最自然的首选分区键是业务日期(如 dt=2024-06-01),因为绝大多数查询都带时间范围。其次可按机构编码或数据域(检验、影像、医嘱)二级分区。但分区不是越多越好:若按患者 ID 分区,会产生海量小分区,元数据服务很快成为瓶颈。
经验规则:单表分区总数控制在数万以内,单分区文件大小目标 128MB~512MB。对于高基数维度(患者、就诊号),应使用分桶(bucketing)或 Z-Order/聚簇,而不是分区。例如按 dt 分区、按 patient_id 分 64 桶,可在点查与范围扫描间取得平衡。
四、数据生命周期管理:从“存得下”到“管得住”
生命周期管理要回答三个问题:数据何时从热转冷、何时可以删除、谁有权提前删除。工程上通常用对象存储的生命周期规则 + 表级 TTL 双轨实现。例如:
规则1: prefix=raw/avro/ → 30 天后转低频,365 天后转归档
规则2: prefix=detail/dt= → 90 天后转低频,730 天后转归档
规则3: prefix=tmp/ → 7 天后直接过期删除
规则4: prefix=archive/ → 启用 Object Lock,合规保留期内禁止删除
需要特别注意的是,小文件合并本身就是生命周期的一部分。流式入湖若每分钟产生一个文件,一天就是 1440 个小文件,查询时元数据开销远超数据读取。应安排每日或每小时执行 compaction,把目标分区重写为 128MB 左右的大文件,并同步更新 Iceberg/Hudi 的元数据快照。
五、医疗数据保留合规要求
医疗数据的保留期限由法规与业务双重决定,常见要求包括:门诊病历通常不少于 15 年,住院病历不少于 30 年,部分影像与病理资料要求长期甚至永久保存;同时《个人信息保护法》《数据安全法》要求最小必要、目的限定,超期后应删除或匿名化。这意味着“归档”不等于“可随意删除”,删除动作必须可审计。
落地建议:
- 为每类数据打上
retention_class标签(如outpatient_15y、inpatient_30y、imaging_permanent),由策略引擎统一驱动。 - 启用对象锁与版本控制,防止勒索软件或误操作删除合规数据;删除请求走审批流并记录操作人、时间、依据。
- 科研与 AI 训练使用的数据应去标识化后再进入分析区,原始标识信息单独隔离存放。
- 定期做“保留策略符合性检查”:抽样核对对象标签、存储类别、到期时间是否与策略一致,输出差异清单并修复。
综合来看,存储层设计的核心是把“格式—分区—生命周期—合规”当成一个整体来调优:Avro 保原始、Parquet 供分析、分区控裁剪、分桶解高基数、生命周期降成本、保留策略守底线。任何一环单独优化,都可能在另一环产生新的瓶颈。