2. 临床数据模型与查询负载分析
临床数据SQL性能优化不能脱离数据模型空谈索引与改写。电子病历、检验、影像、随访四类数据在结构、体量、增长方式和访问模式上差异极大,只有先把模型特征与查询负载对应起来,才能判断一条慢SQL究竟卡在数据量、连接方式还是访问路径上。本节围绕这四类模型展开,并给出负载画像与性能基线的建立方法。
四类临床数据模型的结构特征
电子病历以患者为主实体,围绕就诊(encounter)组织病程记录、医嘱、诊断、护理记录等。典型结构是患者表—就诊表—明细表的三层关系,明细表通过 encounter_id 关联,单次住院可能产生数百到数千条明细。这类模型的特点是连接层级深、时间字段多、文本字段宽,查询常带患者维度与时间范围双重过滤。
检验数据是典型的“主记录+结果明细”结构:一张申请单对应多条检验项目结果,结果表往往包含数值结果、单位、参考区间、异常标志。它的体量增长最快,单家三甲医院检验结果表年增可达数千万到上亿行,且结果值列区分度低、时间分布集中,索引设计需要特别关注组合顺序。
影像数据本身以文件为主,数据库侧通常只存检查登记、报告文本和关键影像索引。它的特点是单行记录不大但报告文本较长,查询多为按患者、检查时间、检查类型检索,且常与病理、检验结果做时间对齐。
随访数据是纵向稀疏结构:同一患者在不同随访时点产生多条记录,字段随随访表单变化,常见做法是宽表或键值对存储。它的查询模式偏向“取某患者最近一次随访”或“按随访时点做趋势对比”,对时间排序和分组取最新的性能敏感。
典型查询模式与访问路径
把负载归为四类后,优化方向就清晰了。点查类如“按患者ID取最近一次检验结果”,理想路径是走 (patient_id, result_time) 组合索引,先定位患者再按时间倒序取第一行,避免全表扫描。范围扫描类如“取某科室某月全部检验结果”,需要评估时间列的选择性,若单月数据占比过高,索引可能不如分区裁剪有效。
聚合类如“统计各病区异常检验项目数”,瓶颈常在分组前的过滤与连接。应先用时间与病区条件缩小结果集,再做分组,必要时用覆盖索引避免回表。多表连接类如“将影像报告与同期检验结果按患者和时间对齐”,连接顺序至关重要:应先过滤出小结果集,再与大表连接,否则中间结果会膨胀数倍。
随访数据的“取最近一次”是高频难点。常见写法是用子查询取 max(follow_time) 再关联,若随访表很大且无合适索引,会反复扫描。更稳的做法是建立 (patient_id, follow_time DESC) 索引,或使用窗口函数一次排序取行号,减少重复访问。
负载画像的采集方法
负载画像不是凭经验猜测,而是从真实执行记录中提取。可从数据库的慢查询日志、性能视图(如 MySQL 的 performance_schema、PostgreSQL 的 pg_stat_statements)中按“执行次数×平均耗时”排序,找出既频繁又慢的语句。对每条语句记录:涉及表、过滤字段、返回行数、执行计划中的扫描方式与连接算法。
采集时要注意区分业务时段。门诊高峰与夜间批处理的负载特征完全不同,混在一起会掩盖真实瓶颈。建议按小时或按业务场景分组统计,形成“时段—语句—资源”的对应关系。
性能基线的建立与使用
基线是后续优化的参照系。建立基线需要固定测试条件:相同数据量、相同并发、相同硬件与参数配置。对每条关键查询记录 P50、P95 响应时间,以及执行计划中的关键算子。基线一旦建立,任何索引调整或SQL改写都应与之对比,判断是真实提升还是波动。
下面是一份可直接使用的性能基线记录表示例,字段按临床查询场景设计:
| 查询编号 | 业务场景 | 涉及表 | 过滤字段 | P50(ms) | P95(ms) | 扫描方式 | 返回行数 |
|---|---|---|---|---|---|---|---|
| Q-EMR-01 | 取患者住院病程 | encounter, progress_note | patient_id, admit_time | 45 | 180 | 索引范围扫描 | 120 |
| Q-LAB-02 | 取最近一次检验结果 | lab_result | patient_id, result_time | 12 | 60 | 组合索引倒序 | 1 |
| Q-LAB-05 | 某月异常项目统计 | lab_result | dept, result_time, flag | 820 | 2600 | 全表扫描+分组 | 35000 |
| Q-IMG-03 | 影像报告与检验对齐 | exam, report, lab_result | patient_id, exam_time | 310 | 1100 | 嵌套循环连接 | 80 |
| Q-FOL-01 | 取最近一次随访 | follow_up | patient_id | 95 | 420 | 子查询+回表 | 1 |
从画像到优化决策
有了模型特征、负载分类和基线数据,优化决策就有了依据。若某条查询执行次数高但单次很快,重点在减少调用次数或加缓存;若执行次数低但单次极慢,重点在改写SQL或调整索引;若执行计划显示全表扫描且过滤后行数很少,说明缺少合适索引;若连接算法选择嵌套循环但驱动表行数很大,说明统计信息或连接顺序需要调整。
需要强调的是,基线不是一次性的。数据量增长、业务规则变化、索引增删都会使基线漂移。建议在每次重大变更后重新采集关键查询的基线,保持参照系有效。临床数据模型与查询负载分析的价值,正在于把“感觉慢”变成“可度量、可定位、可验证”的工程问题。