
“今年除夕,你们必须完成HIS系统从V3.0到V4.0的升级。”
信息科李主任发来这个消息时,老周正在看春节值班表。窗外飘着雪花,办公室里只剩下他一个人。明天就是除夕,大部分同事已经提前请假回家过年了。
老周是昆明软佳的运维负责人,负责XX医院的HIS系统运维。V4.0版本开发了半年,投入了15个开发人员,新功能很多:病历模板云端共享、手术排程智能优化、药品库存预警、移动查房、患者画像、智能分诊…但最关键的,是架构升级——从单体应用变成微服务,理论上更稳定,扩展性更好。
但老周知道,这套系统已经运行了五年,数据量庞大,业务逻辑复杂。数据库里存着三百万患者的完整病历,七年的门诊记录,五年的住院档案,总数据量超过2TB。XX医院是省内最大的三甲医院,日均门诊量一万五千人次,住院病人四千多人,高峰时段并发用户超过2000。任何一点差错,都可能造成医疗事故,甚至引发医疗纠纷,导致医院声誉受损。
“为什么非要除夕?”老周回问。
“因为那天下午后门诊就停了,初二才开诊。”李主任说,”我们有三天窗口期。而且,除夕夜全院最安静,没手术,没急诊高峰,病人少,业务量最低。”
老周沉默了。
说的有道理,但他更知道:除夕夜,工程师们都在家过年,谁愿意加班? 而且,越是”安静”的时候,越容易麻痹大意。平时医院人来人往,任何异常都能及时发现;除夕夜如果出问题,可能到初二上班才暴露,那会已经酿成事故,影响初三的学术会议——院长要在会议上展示新系统,给医院”长脸”。
“能不能预约年初三?”老周问。
“不行,初三有学术会议,院领导和外宾都在。系统要展示新功能,我们要在全同行面前亮相。”
老周明白了:这不是单纯的技术问题,是政治任务,是面子工程。院长要在学术会议上展示HIS系统升级成果,给医院加分,给信息科长脸。
2. 升级前的”恐吓式”测试
老周带着团队,先做了一件事:模拟灾难。
他们在测试环境,把V4.0版本部署上去,然后人为制造各种故障场景,看系统能否扛住。
测试环境的数据量是生产环境的10%(200GB),但架构完全一致。
– 场景一:数据库突然断电
模拟数据库服务器宕机,看应用能否优雅降级。结果:所有功能全部不可用,微服务全部报错。因为所有服务都依赖数据库,而数据库挂了后,服务注册中心(Nacos)也挂了(它也依赖数据库),微服务之间互相找不到,整个系统雪崩。
– 场景二:网络突然中断
拔掉其中一台应用服务器的网线。结果:那台服务器上的所有请求失败,但没有自动迁移到其他服务器。负载均衡器虽然检测到服务器不可用,但需要30秒才能剔除,这期间用户请求都会失败,体验极差。
– 场景三:某个微服务突然崩溃
手动kill掉”医嘱管理”服务。结果:所有依赖这个服务的上游功能(如病历书写、护理记录、检查申请)全部报错。熔断器(Hystrix)配置了,但阈值设得太高——需要100次错误才触发,而在这之前,上游已经堆积了大量错误,线程池被打满。
– 场景四:磁盘突然写满
模拟日志磁盘爆满。结果:系统开始抛出大量IOException,但错误没有统一处理,用户看到的是”系统异常”,而不是”服务器繁忙,请稍后重试”。没有降级策略。
– 场景五:GC停顿
模拟Full GC,暂停30秒。结果:所有请求超时,用户感觉”卡住了”。
老周的头大了。
这些都不是V3.0时代会遇到的问题——V3.0是单体应用,数据库不挂,系统就不挂。现在V4.0拆成十几个微服务,一个环节出问题,可能影响一片功能。微服务的复杂性,远超预期。
3. 我们制定了三套”保底方案”
老周给李主任打了个电话:”直接升级风险太大。我建议分三步走,每一步都有回退方案,确保业务绝对不中断。”
第一步:增量上线,不是全量切换
– 先在门诊药房试点,只对药房人员开放新系统,其他科室继续用旧系统
– 试点稳定三天后,再扩大范围到门诊收费、住院收费
– 最后全员上线
“这样可以控制风险范围,即使药房出问题,也只是局部影响,不影响整个医院。”
第二步:数据双写,随时能回退
– 春节期间,新旧系统并行运行
– 所有新业务数据,同时写入新旧两个数据库
– 如果新系统出问题,一秒回退到旧系统,数据不丢
“数据一致性怎么保证?”李主任问。
“我们在应用层做双写,用一个事务同时写两个库。如果其中一个写失败,整个事务回滚。而且我们会做定时对账(每半小时一次),发现不一致立即修复。双写最多保持一周,等新系统稳定了,就切换单写。”
第三步:除夕不升级,只做”预演”
– 除夕当天,我们不碰生产环境
– 在测试环境,完整演练一遍升级流程和回滚流程
– 如果演练顺利,年初二晚上做真实升级
“为什么不在除夕升级?”
“因为除夕全员都在家,万一出事,人手不足。年初二大家已经收假,可以应对突发情况。”
李主任沉默了很久,思考这个方案的利弊。
“如果年初二升级失败,初三学术会议展示什么?”
“展示我们之前双写的旧系统数据。新系统没上线,但升级计划已经在执行中,可以汇报进度,说明我们在扎实推进。”老周说。
李主任终于同意了:”行,就按你说的来。但年初二必须成功,不然院长会发飙,我们大家都不好过。”
4. 那个熬了三天的夜晚
年初二晚上八点,升级正式开始。
老周团队八个人,加上信息科三个人,全部在现场。机房温度有点低,但每个人都精神高度紧张,手里拿着对讲机,随时沟通。
升级步骤详细到分钟,印在每个人的手里:
1. 数据库备份(预计30分钟):全量备份 + 校验和比对
2. 部署V4.0新服务(预计60分钟):13个微服务逐个启动、初始化、健康检查
3. 数据迁移(历史数据从旧表结构迁移到新表结构,预计120分钟):涉及2176张表,2.3TB数据
4. 配置切换(DNS、负载均衡切到新服务,预计15分钟)
5. 功能验证(各科室核心功能验证,预计60分钟):挂号、收费、住院登记、医嘱、药房…
计划总时长:285分钟,也就是四个半小时。
看起来时间很充裕。
但老周知道,计划赶不上变化。他们准备了”升级失败回滚预案”,如果任何一步出问题,60分钟内必须回滚,否则数据不一致,回滚会更麻烦。回滚本身也需要时间。
第一步:数据库备份。顺利。
虽然备份速度比预期慢10%(用了45分钟),因为数据量比预想大20%,但还是在计划内完成,并校验了checksum,无错误。
第二步:部署V4.0新服务。顺利但有波折。
微服务启动时,有2个服务启动失败:配置管理服务(config-server)因为端口6380被占用(旧系统有个监控进程),注册中心(nacos)因为数据库连接字符串写错了(少了个分号)。修改后重试,总共花了75分钟,比计划多15分钟。
第三步:数据迁移——这是最关键的一步,也是风险最大的。
历史数据有七年的门诊数据、五年的住院数据, Tablespace 超过 2TB。迁移工具data-migrator是公司自己开发的Java程序,还没在这么大的数据集上验证过。
“开始迁移。”
进度条:0.1%…0.2%…
时间一分一秒过去,大家都盯着屏幕,不敢说话。
一百分钟后,进度条卡在37%。
“停一下。”老周心里一紧。
运维工程师小王脸色很难看:”迁移速度变慢了,从每分钟1%降到每分钟0.1%。可能遇到数据热点,或者某张表有锁,或者磁盘IO达到瓶颈。”
“什么表?”
“医嘱表,数据量最大的表,四亿多条记录,占总数据量的60%。现在卡在这一步,因为医嘱表有外键约束,其他表都在等它完成。”
老周拳头捏紧了,指甲嵌进肉里。
37%的数据已经迁过去了,如果中断,回滚要删除这些数据,很麻烦;如果不回滚,继续迁,但速度这么慢(0.1%/分钟,意味着还需要6天),到天亮也迁不完,初二肯定上不了线。
“能不能跳过医嘱表,先迁其他表?”
“不行,医嘱表被其他几十个表外键约束。如果医嘱表没迁移成功,其他表迁了也联不起来,数据是断的,对账都对不上。”
会议室里,气氛凝重。已经凌晨一点,窗外偶尔传来鞭炮声——有人在提前过年。
已经是凌晨一点。
老周看向大家,眼神坚定:”还有什么想法?不论多大胆,说出来。”
5. 最后的办法:物理复制
小王,这个26岁的年轻工程师,说了一个大胆的想法:”我们不做逻辑迁移了,用物理复制。”
“什么意思?”
“我们不通过工具逐条迁移数据,而是直接把旧数据库的 MDF/LDF 文件拷贝到新数据库服务器,在新库上直接做 schema 转换。”
这相当于把旧数据库的”硬盘”直接物理搬到新数据库,然后在新数据库上修改表结构,适应V4.0的 schema。
因为只是修改表结构(加字段、改索引),不移动数据行,速度会快很多——复制2.3TB文件,通过内网万兆光纤,只需要30分钟;schema转换再花1小时。总共2小时搞定。
但风险是:
– 物理复制过程中,如果旧库还有数据写入(虽然升级期间已经通知停业务,但万一有漏网的终端还在连接),数据会不一致。
– 新旧数据库的字符集、排序规则必须完全一致,否则会乱码。
– 复制后需要重新统计信息,否则查询性能会下降,相当于”数据迁移了,但查询更慢了”。
“赌一把。”老周说。现在没有其他选择,时间不等人。
他们先命令所有终端停止连接数据库,确保业务完全停止——这一点至关重要,确保了物理复制的ACID。
然后,停止旧数据库服务,用Robocopy工具拷贝数据文件,保留所有权限和属性。
拷贝花了20分钟(2.3TB通过内网万兆,速度比预想快)。
接着,在新数据库上运行 schema 转换脚本,把旧表结构改造成新表结构。这个过程要极其小心:不能丢失数据,要处理字段类型变化(如VARCHAR长度变化)、新增字段默认值、索引重建…
30分钟搞定。
接着,启动新数据库,验证数据一致性。
比对脚本跑了一个小时,结果是:一致性 99.99%,有少量数据不一致(约0.01%,约230万条记录中的23条),但都是升级期间产生的”残留”数据(停业务后最后几分钟的操作,有的写一半,有的锁未释放),我们可以从binlog里补回来。
老周看了看表:凌晨三点四十分。
“继续!”他的声音沙哑,但坚定。
6. 天亮前的最后一道坎
数据迁移完成,已经是早上六点,天蒙蒙亮。
下面就是配置切换, cutover 到新系统。
但就在这时,医务科刘主任打来电话,语气焦急:”有几个科室反映,他们电脑登录新系统特别慢,要半分多钟。医生在急着开医嘱,病人等在排队,护士站骂人了。”
老周心里一沉。
“是不是网络问题?”
“不是网络,是新系统启动后,有些服务初始化慢。特别是’患者基本信息查询’这个服务, cold start 要一分钟。很多医生在开机后第一次查询,要等很久,他们没耐心。”
老周突然想到:”我们不是有双写吗?让这些科室的人先用旧系统,我们调优新系统。”
但问题是,有些功能V4.0才有,旧系统用不了,医生会抱怨新功能不能用。
“能不能手动调整那些慢服务的超时时间,先让他们能登录?”
小王试了一下,调整了JVM堆内存(从2G加到4G)和线程池参数(从50加到100),登录时间从50秒降到了15秒。
“先这样,赶不上初一,初二能上线就不错了。”老周安慰自己,但心里知道,用户体验不能一直这样凑合。
7. 大年初二,系统上线了
上午十点,老周带着运维团队,在医院信息科”坐镇”。
李主任也在,脸色紧张。他身后站着医务科、护理部、财务科的人,都在等消息。
各科室开始有人陆续上班,系统正式开放使用。
第一个问题是在十点二十分钟出现的:收费处小张打不开收费界面,提示”服务不可用”。
运维立即排查:是”收费服务”这个微服务挂了,因为内存溢出(OOM),JVM heap 满了。
分析堆 dump,发现是某个收费记录的数据量异常大(超过10万条明细),导致内存泄漏。
临时方案:重启服务,并设置单笔交易明细上限为1000条,超过则提示”数据过多,请分批处理”。
十一点,药房反映,药品库存数量不对,有些药显示有库存,实际药架上没药。
查日志:数据迁移时,有一批药房的库存流水没迁全——因为那条记录的状态字段是NULL,迁移脚本跳过了NULL值。
紧急从旧库补数据,手动执行SQL,花了20分钟。
十二点,住院处反映,有病人出院结算时,总金额多了一块二毛钱。
查对账系统:有一笔三毛钱的二维码支付手续费,V3.0没算进总金额,V4.0算了(新功能自动计算)。
热修复:在结算时,如果金额与旧系统差异<1元,自动以旧系统为准。
下午三点,所有问题基本解决,系统运行平稳。
老周给李主任发了消息:”系统基本稳定,可以对外宣称升级完成了。”
李主任回复:”好。但学术会议还有半小时开始,院长要展示新功能,你们那边准备好了吗?”
老周深吸一口气,在微信群里发了消息:”所有工程师,保持手机畅通,随时待命。系统暂时稳定,但别掉以轻心。”
8. 为什么升级总是这么惊险?
升级完成后第三天,老周写了长篇复盘报告,发给公司管理层和XX医院信息科。
他发现,这次升级之所以这么惊险,不是因为技术难度大,而是因为:
1. 想一次性完成:没有采用渐进式上线,而是”一夜切换”。如果分阶段(先药房、再收费、后住院),问题可以早发现早解决,不会最后搞”大杂烩”。
2. 数据迁移工具没经过大数据验证:37%的迁移速度就已经暴露出性能问题,说明工具在TB级数据上表现不佳,应该用更成熟的方案(如物理复制)。
3. 冷启动问题没预判到:新服务启动慢,影响用户体验,特别是首次查询。应该有预热机制(提前启动,加载缓存)。
4. 测试环境数据量不到生产环境十分之一:所以没遇到真实场景的性能瓶颈和脏数据问题。测试应该用生产数据的脱敏副本。
5. 应急预案不够细:虽然准备了回滚方案,但执行时发现很多细节没考虑到(如回滚后的数据一致性验证)。
改进措施(老周在报告中详细列出):
1. 未来升级,必须先灰度发布,小范围验证(如先上10%流量,观察24小时)
2. 数据迁移工具,必须在与生产环境同量级的数据集上测试(至少1TB),并准备物理复制作为备选方案
3. 服务预热机制:在切换前2小时,提前启动新服务,完成JIT编译和缓存预热
4. 升级期间,必须有物理备份,随时能回滚到上一秒状态
5. 建立”升级检查清单”,逐项打勾,不跳过任何步骤
6. 每个微服务都要有熔断、降级、超时配置,不能依赖”默认值”
7. 升级窗口期要预留buffer,计划6小时的任务,给10小时
9. 事后,李主任说了一句话
一周后,李主任请老周吃饭,地点在医院食堂的小包间,没叫外人。
“这次升级,虽然出了不少问题,但总体是成功的。”李主任说,”最重要的是,我们没有因为升级导致病人看病受阻。初三学术会议,院长展示了新系统,效果很好。院长说:’你们的信息科,能打硬仗。'”
老周松了口气。
“但我有个问题,”李主任又说,露出苦笑,”下次升级,能不能别选春节?我们科的人也要过年,连续三天熬夜,身体受不了。”
老周笑了:”下次,我建议选五一或十一,窗口期更长,我们也有更多时间做灰度验证,不用赶工期。”
李主任点头:”这个提议,下次班子会我会提。顺便,你们那套’双写+对账’方案,效果不错,数据零丢失。我们想把它固化下来,以后日常也跑,作为实时备份。”
“可以,我们会写成功能模块,纳入标准产品。”
10. 稳定压倒一切
老周后来在部门内部分享会上,反复强调,把这起事件作为反面教材和成长案例:
“系统升级最大的风险,不是技术问题,是时间压力。
时间一紧,人就容易慌,容易漏步骤,容易不走检查清单。
但系统升级,最怕的就是’赶’。
宁可慢一点,稳一点,分阶段上,也不要一次性能完成但风险不可控。
稳定压倒一切。业务连续性,比面子、比会议、比展示,都重要得多。
这次除夕升级,教训是深刻的。我们学到了:
– 不要相信’理论上’,一定要测试验证,尤其是灾难恢复测试
– 不要跳过检查清单,每一步都要有记录、有责任人、有回滚方案
– 要有回滚预案,而且回滚方案本身也要测试过
– 时间缓冲要给足,计划再乘以1.5的系数
– 升级不是IT部门的事,是全院的事,业务部门要参与演练
工程是严谨的科学,不是冲刺。冲刺得来的成功,往往是隐患的开始。”
互动话题
你经历过最惊险的一次系统升级是什么情况?有什么经验教训?
> 基于真实医院场景改编,人物均为化名
立即免费试用门诊系统:https://app.kmhis.com/
International Version:https://app.kmhis.com/multi/
了解软佳门诊管理系统详情:https://www.kmhis.com/outpatient-management-system.html
支持8种语言:简体中文、繁体中文、香港中文、English、藏文、泰文、老挝语、越南语
说真的。这类问题我见过太多了。每次看到医院同事为选型头疼。我就想,要是早点有人把这些经验分享出来就好了。毕竟。选择不对。后面全是麻烦。选择对了。省心省力。还能提升整个机构的运行效率。希望这篇能帮到正在纠结的你。
你如果有具体需求。也可以去 www.kmhis.com 看看。那里有更详细的技术方案和案例。
