电子病历系统并发写入卡顿,根子多半在磁盘IO,解决思路不是无脑加硬件,而是先定位IO瓶颈类型,再针对性地优化数据库、文件系统和存储选型。
电子病历系统并发写入慢,磁盘IO到底卡在哪
医院门诊早高峰的场景,相信HIS工程师都不陌生,医生开完医嘱点击保存,护士站批量录入生命体征,药房同时发起发药确认,几百个会话在同一秒内涌向数据库,这个时候,电子病历系统的响应时间从平时的几百毫秒飙升到几秒甚至几十秒,多数情况下,问题不出在CPU,也不出在内存,而是磁盘IO扛不住了。
并发写入和顺序读写的本质区别
数据库的写入操作和读操作有本质区别,读操作可以靠内存缓存大幅加速,命中缓存后根本不需要碰磁盘,但写入操作不一样,为了保证事务的持久性,数据库必须把redo log刷到磁盘上,事务才能提交成功,这个动作叫做fsync,每一个提交的事务,哪怕只改了一行数据,也要等待一次磁盘写入完成。
当并发量上来以后,大量事务同时提交,磁盘就要处理一个又一个的随机小IO请求,机械硬盘的寻道时间大约在几毫秒到十几毫秒之间,每秒能处理的IOPS也就一两百,面对几十个并发写请求,磁盘队列直接被打满,后续请求只能排队等待,这就是电子病历系统并发写入慢最常见的原因。
数据库层的写放大效应
问题还不止于此,PostgreSQL的MVCC机制,Oracle的回滚段,MySQL的Change Buffer,这些机制在带来并发控制能力的同时,也放大了写入量,一条UPDATE语句,可能产生新的数据版本、回滚日志、索引更新、WAL日志,算下来,逻辑上一条写入,物理上可能要写2到3个地方。
行业共识认为,数据库层的写放大系数通常在2到4倍之间,这就意味着,业务上每秒1000次写入,磁盘实际承受的可能是每秒3000次IO请求,如果索引建得过多,或者表结构设计不合理,这个放大系数还会更高。
一个典型的门诊高峰场景还原
想象一下这样的场景:上午十点,某三甲医院的内科诊区,200个诊室同时开诊,每个医生每5分钟开出一张处方,每张处方涉及主表、明细表、药品表、收费表等多个表的写入,加上护士站的操作,整个医院的写入TPS大概在500左右,如果数据库服务器用的是两块SATA SSD组成的RAID 1,理论IOPS大概在几千,表面上看够用了,但实际运行中,由于写放大、索引维护、后台检查点等因素,磁盘利用率经常冲到90%以上。
这个时候,电子病历系统的操作响应就会变得非常不稳定,医生点保存,转圈好几秒,然后整个诊区开始抱怨系统卡,这是医院信息化建设中相当有代表性的一个场景。
医院HIS系统磁盘IO压力测试怎么做
既然要解决问题,先得把问题量化,很多医院的运维团队面对卡顿的第一反应是加内存、加CPU,但加完之后效果不明显,正确的做法是先做压力测试,搞清楚磁盘IO的真实水位。
用系统命令快速定位IO瓶颈
登录数据库服务器,先用iostat -x 1看看磁盘的实时状态,重点看几个指标:
- %util:磁盘忙绿百分比,持续超过80%说明磁盘是瓶颈
- svctm:平均服务时间,机械盘一般在5到10毫秒,SSD应该低于1毫秒
- await:平均等待时间,这个值如果远大于svctm,说明请求在排队
- aqu-sz:平均队列长度,持续大于2说明负载偏高
再看iostat -x 1输出的wr_s和wKB/s,如果每秒写入次数只有几百,但%util已经100%,说明单次写入的代价很大,问题可能出在写入模式上,如果wr_s本身就很高,比如超过2000,那确实是并发量太大。
跟踪数据库等待事件
操作系统层面的指标只能告诉我们磁盘忙不忙,要弄清楚谁在等磁盘,还得看数据库的等待事件。
Oracle数据库可以查v$system_event视图,重点看log file sync和log file parallel write两个事件。log file sync的平均等待时间如果超过20毫秒,说明提交事务时写redo log太慢,MySQL可以用SHOW ENGINE INNODB STATUS,看Log flush相关的状态,PostgreSQL则查pg_stat_database里的xact_commit和blks_written。
如果等待事件集中在日志写入上,优化方向应该是提升日志盘的性能,如果集中在数据文件的写操作上,那就要考虑数据文件布局和缓冲池设置。
医院信息科能做的三个压测动作
停诊后压测,晚上门诊结束后,在测试库上模拟第二天的并发写入,用sysbench或者pgbench这类工具,按医院实际业务的写入比例构造混合负载,从100并发开始,逐步往上加,每档跑10分钟,记录TPS和延迟的拐点。
抓取真实SQL,打开数据库的慢查询日志,把白天高峰时段的TOP 20写入SQL抓出来,分析这些SQL涉及哪些表,索引是否合理,有没有跨事务的多表更新,往往能发现某个SQL有全表扫描,写操作被放大。
对比测试,把同样的压测脚本跑在当前的机械盘阵列上,再跑在一块临时挂载的NVMe SSD上,对比两次的TPS和平均延迟,差值就是存储硬件提升带来的收益,这个数据可以作为后续采购预算的依据。
电子病历系统选型时磁盘IO该怎么看
很多医院在选型电子病历系统时,注意力都在功能列表上:有没有临床路径、有没有单病种质控、病历编辑器好不好用,但磁盘IO的支撑能力同样重要,它决定了系统在高并发时能不能保持稳定的体验。
共享存储和本地盘的选择
医院核心业务系统一般用共享存储,方便做双机热备和RAC,共享存储的性能取决于存储设备的控制器能力和后端磁盘配置,全闪存阵列的性能很好,但价格也不低,混闪阵列如果缓存命中率高,大部分读操作可以在缓存中完成,写入压力则集中在后端磁盘。
选择共享存储时,重点关注控制器的写缓存策略,如果写缓存带电池保护,写入可以先落缓存再异步刷盘,性能会好很多,如果没有电池保护,每次写入都要穿透到后端磁盘,IOPS会大打折扣。
SSD的选型要看两个指标
第一是耐久度,电子病历系统的写入量虽然比不上互联网业务,但数据库的redo log每天都在持续写入,选SSD时要看DWPD(每天全盘写入次数),企业级SSD一般是1到3,消费级只有0.1到0.3,长期用下来,消费级SSD很容易提前报废。
第二是稳态性能,SSD刚拿到手时性能很好,但写满一部分后,垃圾回收机制会让写入性能明显下降,选型时不要看峰值IOPS,要看持续写入状态下的稳定IOPS,这个数据在厂商的规格书里一般叫"稳态随机写入IOPS"。
国产数据库替换过程中的IO性能变化
近年来,不少医院在推进国产数据库替换,从Oracle迁移到国产数据库,SQL语法兼容性是一方面,IO行为的变化同样不能忽视。
国产数据库大多基于PostgreSQL或MySQL内核演进而来,以PostgreSQL内核的国产数据库为例,它的WAL写入机制和Oracle的redo log有明显差异,PG的每个事务提交都要写WAL,而Oracle有group commit优化,迁移之后,同样的业务量下,磁盘的写入次数可能会上升。在迁移前一定要做IO压测对比,不要只比功能。
还有一个容易被忽略的点:国产数据库的默认参数和Oracle不同,比如checkpoint间隔、WAL缓冲区大小、commit延迟设置,这些参数对磁盘IO影响很大,需要按照医院的硬件配置做针对性调整。
磁盘IO优化的四个实战方向
诊断清楚瓶颈之后,优化可以从四个方向入手,不需要一次性全部做完,按投入产出比排序,逐项推进。
减少数据库层的写放大
- 精简冗余索引,每多一个二级索引,每次INSERT和UPDATE就要多写一棵B+树,把长期未使用的索引删掉,写放大立刻下降
- 调整WAL/redo log的group commit参数,让多个事务的提交合并成一次日志刷盘,大幅降低fsync次数
- 对电子病历系统中频繁更新状态的表,考虑将热点字段拆分到独立的表中,避免整行更新导致的大字段重写
优化文件系统层
- 挂载参数中开启
noatime,避免每次访问都更新atime - 如果使用PostgreSQL,WAL日志建议放在独立的磁盘分区上,避免和数据文件争抢IO
- XFS和ext4在并发写入场景下表现有差异,XFS对多线程并发写入的扩展性更好,新部署的系统可以考虑
升级存储硬件
这是最直接见效也最费钱的方式,升级路径一般是:
| 现状 | 升级方案 | 预期收益 |
|---|---|---|
| 机械盘RAID 10 | 换成SATA SSD | IOPS提升5到10倍 |
| SATA SSD | 换成NVMe SSD | 延迟降低一半以上 |
| NVMe SSD | 换成带掉电保护的NVMe | 稳定性提升,寿命更长 |
| 单机本地盘 | 换成全闪存阵列 | 性能和可靠性同时提升 |
应用层削峰填谷
有些写入操作可以延后处理,不需要实时落库,比如电子病历的自动保存功能,可以让前端每30秒自动保存一次,但如果后台每30秒就发起一次真实的数据库写入,压力还是很大,合理的做法是前端把自动保存的内容放在本地内存或localStorage,只有用户手动保存或切换页面时才真正写入数据库。
类似的,护理记录单的批量录入,可以先在前端汇总,一次性提交,减少数据库的往返次数,对降低IO压力有明显帮助。
磁盘IO监控的日常巡检建议
优化不是一次性的事情,磁盘IO的压力会随着业务量的增长而变化,建立日常巡检机制,把问题扼杀在萌芽期。
- 每周检查一次数据库服务器的
iostat输出,关注磁盘利用率趋势是否在逐步上升 - 每月分析一次数据库的AWR或类似报告,看TOP等待事件有没有变化
- 每季度做一次IO压测,对比当季的性能基线和上季度是否有明显下降
- 关注存储告警:SSD的剩余寿命、RAID组的降级状态、存储控制器的缓存命中率
相关问答
问:电子病历系统并发写入慢,加内存有用吗?
数据库缓冲池扩大确实能提升读操作的缓存命中率,但写入操作必须落盘才能保证持久性,如果磁盘本身已经是瓶颈,加内存只会让更多的脏块积压在内存里,最终还是要刷到磁盘上,效果有限,应先确认磁盘利用率,再决定是加内存还是换存储。
问:医院HIS系统磁盘IO压力测试需要停机吗?
测试分两档,在测试库上做压力测试不需要停机,可以随时进行,如果想测试生产环境的真实承载能力,建议在门诊结束后的夜间窗口期进行,控制并发数,避免影响住院部的夜间业务,测试前做好数据库备份,测试过程中持续监控CPU、内存和IO指标,异常时立即停止。
问:NVMe SSD和SATA SSD在医院业务系统里的实际差距有多大?
顺序读写场景下差距不大,SATA SSD的500MB/s已经够用,差距主要体现在随机小IO和并发写入上,NVMe的低延迟优势明显,对于电子病历系统这种频繁小事务写入的场景,NVMe的体验提升相当可观,不过共享存储架构下,瓶颈可能在存储控制器,单独换服务器内的NVMe盘不一定能发挥全部性能。