医疗数据ETL开发流程总结
目录
- 一、整体架构
- 二、完整ETL流程(以“每日抽取门诊数据”为例)
- ▶️ 阶段1:数据准备(Windows 侧)
- ▶️ 阶段2:ODS层抽取(Linux + Kettle + Shell)
- Crontab 触发(每天凌晨1点)
- Shell 脚本 run_daily_etl.sh
- Kettle 作业 load_ods_visit.kjb
- ▶️ 阶段3:STD层转换(Linux + 存储过程)
- ▶️ 阶段4:报表层(可选)
- 三、安全与合规考虑(医院特别重要!)
- 四、总结:医疗项目标准执行流程
一、整体架构
技术栈:Kettle;MySQL;Linux命令;Shell脚本
在医院真实部署环境下(内网隔离、资源有限、安全合规),如何设计一个完整、可落地的医疗数据项目架构?
✅ 一台 Linux 服务器装数据库(DB)
✅ 一台 Windows 服务器用于接口调测、开发、临时文件处理等
✅ Kettle 建议部署在 DB 服务器(Linux)上,以减少网络传输、提升性能、便于管理
这里暂不考虑 Python 脚本(虽然它很强大,但有些项目确实不需要)。整体架构如下:
┌──────────────────────────────────────────────┐
│ Windows 服务器(开发/调测机) │
│ - 接口调测(Postman/Swagger) │
│ - 临时文件存放(CSV/Excel) │
│ - Spoon 图形化开发(可选) │
│ - 日常运维脚本(可选) │
└───────────────┬──────────────────────────────┘│ 文件传输(SFTP/共享目录)↓
┌──────────────────────────────────────────────┐
│ Linux 服务器(生产DB + ETL) │ ← Kettle部署在这里!
│ - 数据库(Oracle/MySQL/PostgreSQL) │
│ - Kettle(Spoon/Kitchen/Pan) │ ← 核心调度引擎
│ - Shell 脚本(调度、文件处理、日志清理) │
│ - 存储过程(数据清洗、转换、同步) │ ← 核心数据逻辑
│ - Crontab 定时任务 │ ← 生产调度
│ - 日志目录 /logs │
│ - 临时数据目录 /data/in, /data/out │
└──────────────────────────────────────────────┘↓
┌──────────────────────────────────────────────┐
│ 医院业务系统(HIS/LIS/PACS等) │
│ - 视图 / 接口 / 数据库表 │ ← 数据源
└──────────────────────────────────────────────┘
二、完整ETL流程(以“每日抽取门诊数据”为例)
▶️ 阶段1:数据准备(Windows 侧)
HIS 系统每日凌晨生成门诊数据视图或导出 CSV;
通过 SFTP / 共享目录 / 数据库直连,将数据文件或访问权限提供给 Linux 服务器;
# 示例:从Windows共享目录拷贝文件到Linux
scp windows_user@windows_ip:/data/out/visit_*.csv /data/in/
✅ 为什么在Windows准备?
医院很多老系统只支持 Windows 导出,或接口调试工具(如 SOAPUI)只能在 Windows 运行。
▶️ 阶段2:ODS层抽取(Linux + Kettle + Shell)
Crontab 触发(每天凌晨1点)
0 1 * * * /scripts/run_daily_etl.sh
Shell 脚本 run_daily_etl.sh
#!/bin/bash
RUN_DATE=$(date -d "yesterday" +%Y-%m-%d)
LOG_FILE="/logs/etl_${RUN_DATE}.log"echo "[$(date)] 开始ODS抽取" >> $LOG_FILE# 调用Kettle作业(加载CSV或直抽视图)
/opt/kettle/kitchen.sh \-file=/etl/jobs/load_ods_visit.kjb \-param:RUN_DATE="$RUN_DATE" \-level=Basic >> $LOG_FILE 2>&1if [ $? -ne 0 ]; thenecho "[$(date)] ODS抽取失败!" >> $LOG_FILEexit 1
fi
详细解释:
🍳 1. /opt/kettle/kitchen.sh
—— 什么是 Kitchen?
✅ Kitchen 是 Kettle 的“作业(Job)执行器” —— 命令行工具,用于运行
.kjb
文件。
- Kettle 有两个核心命令行工具:
- Pan.sh → 用于执行 转换(Transformation),即
.ktr
文件; - Kitchen.sh → 用于执行 作业(Job),即
.kjb
文件;
- Pan.sh → 用于执行 转换(Transformation),即
👉 类比理解:
.ktr
(转换) = 一道菜的做法(比如“红烧肉怎么做”).kjb
(作业) = 一桌宴席的流程(“先上凉菜→再上热菜→最后上汤”)- Pan = 厨师,负责做一道菜(.ktr)
- Kitchen = 总厨/调度员,负责安排上哪几道菜、顺序、失败怎么办(.kjb)
✅ 适用场景:
- 当你需要调度多个转换、控制流程、处理错误、发邮件、循环、条件判断时 → 用
.kjb
+kitchen.sh
- 当你只需要执行一个数据转换(如从A表读、清洗、写入B表) → 用
.ktr
+pan.sh
🔧 2. -param:RUN_DATE="$RUN_DATE"
—— 参数传递
✅ 这是向
.kjb
作业传入一个命名参数(Named Parameter),名为RUN_DATE
,值为 Shell 变量$RUN_DATE
。
📌 Shell 层:
RUN_DATE=$(date -d "yesterday" +%Y-%m-%d) # 如 2025-09-23
📌 Kettle 层(在 .kjb 或 .ktr 中):
- 在 Spoon 中,你可以在“作业设置”或“转换设置” → “参数” 中定义
RUN_DATE
; - 在 SQL 或“表输入”中,你可以使用
${RUN_DATE}
:SELECT * FROM his_visit_view WHERE visit_date = '${RUN_DATE}'
✅ 为什么用参数?
- 避免硬编码日期;
- 支持补数据(如重跑 2025-09-01);
- 同一个作业可被不同调度策略调用(日跑、周跑、手工跑);
📝 3. -level=Basic >> $LOG_FILE 2>&1
—— 日志输出控制
我们拆开看:
➤ -level=Basic
设置 Kettle 执行时的日志级别。
可选值:
Nothing
:不输出任何日志;Error
:只输出错误;Minimal
:最少信息;Basic
✅:推荐生产环境使用 —— 输出作业/转换开始结束、记录数、关键步骤;Detailed
:输出每一步的详细信息;Debug
:调试级,输出变量值、内部状态;Rowlevel
:输出每一行数据(慎用,日志爆炸!)
➤ >> $LOG_FILE
将标准输出(stdout) 追加写入日志文件(如
/logs/etl_2025-09-23.log
)
➤ 2>&1
将标准错误(stderr) 重定向到标准输出,最终也写入同一个日志文件。
✅ 合起来的意思:
“以 Basic 级别运行,把所有正常输出和错误信息都追加写入日志文件,方便事后排查”。
✅ 总结:一句话记住核心概念
Pan 跑 .ktr(转换数据),Kitchen 跑 .kjb(调度流程),参数用 -param,日志用 -level + >> 2>&1 —— 生产调度就这么配!
Kettle 作业 load_ods_visit.kjb
作业执行流程如下:
步骤1:表输入 → 直接写SQL查询HIS视图(或读CSV文件);
步骤2:数据清洗 → 字段改名、空值处理、类型转换(可用“字段选择”、“计算器”等);
步骤3:表输出 → 写入 ods_patient_visit 表;
步骤4:SQL脚本 → 执行 UPDATE rs_ods.history SET flag=1 WHERE …;
(可选)步骤5:发送邮件 → 失败时通知负责人;
✅ 为什么Kettle放Linux?
1.减少跨机数据传输(直接连本地DB);
2.避免Windows性能瓶颈(尤其大数据量);
3.Linux更稳定,适合7x24小时运行。
▶️ 阶段3:STD层转换(Linux + 存储过程)
Kettle 作业继续执行(或独立作业)
调用存储过程:
CALL update_std_patient_visit('${RUN_DATE}');
存储过程示例(数据库内执行)
CREATE PROCEDURE update_std_patient_visit(IN p_date DATE)
BEGIN-- 1. 清洗标准化UPDATE ods_patient_visitSET gender = CASE WHEN gender NOT IN ('男','女') THEN '未知' ELSE gender END,dept_code = LPAD(dept_code, 6, '0')WHERE visit_date = p_date AND ods_flag = 0;-- 2. 插入STD层(关联维表)INSERT INTO std_patient_visit (patient_id, dept_name, visit_date, age_group)SELECT o.patient_id,d.dept_name,o.visit_date,CASE WHEN o.age < 18 THEN '儿童' ELSE '成人' ENDFROM ods_patient_visit oJOIN dim_department d ON o.dept_code = d.dept_codeWHERE o.visit_date = p_date AND o.ods_flag = 0;-- 3. 更新状态UPDATE ods_patient_visit SET ods_flag = 1 WHERE visit_date = p_date AND ods_flag = 0;
END;
✅ 为什么用存储过程?
1.数据库内计算,性能极高;
2.事务保证数据一致性;
3.与表结构紧耦合,易维护。
▶️ 阶段4:报表层(可选)
可直接供 BI 工具(如帆软、Tableau)连接 STD 层出报表或写入数据集市供临床科研使用。
💡 Spoon(图形界面)要不要装在Linux?
生产环境:不需要,用 Kitchen 命令行即可;
开发调试:可以装(Linux也有Spoon),但很多工程师习惯在Windows用Spoon开发,然后上传.ktr/.kjb到Linux执行 —— 完全可行!
三、安全与合规考虑(医院特别重要!)
网络隔离:Linux DB服务器不对外网开放,仅内网访问;
权限最小化:Kettle连接DB使用只读账号(抽ODS)或最小权限账号;
数据脱敏:存储过程中对敏感字段(身份证、电话)进行脱敏处理;
日志审计:所有ETL操作记录日志,保留6个月以上;
备份机制:每日备份ODS/STD层关键表;
变更管理:所有脚本、存储过程、KTR文件纳入Git版本控制。
四、总结:医疗项目标准执行流程
⏰ 凌晨x点 → Crontab触发Shell脚本 → 调用Kettle作业 → ODS抽取 → 调用存储过程,清洗ODS数据并插入STD → 更新日志 → 完成
🧭 执行口诀:
Windows 准备数据源,
Linux 扛起ETL大旗,
Kettle 调度不缺席,
存储过程干脏活累活,
Shell 脚本打配合,
Crontab 定时稳如钟,
日志历史表来兜底,
医院项目稳落地!