一次 HikariCP 连接池耗尽导致的线上雪崩排查实录——完整排查报告

一、执行摘要

维度 详情
问题发生时间 2026-07-27 09:05 ~ 09:50
核心现象 大部分接口响应超时/不可用,租户侧发药、HIS 同步、查询类接口大量失败
直接异常 Lock wait timeout exceeded(314 次)、MyBatisSystemException(大量)
主要受影响表 ihm_order_prescription
根因定位 直接原因:核心服务 HikariCP 连接池容量过小(默认仅 10),早高峰并发请求迅速耗尽连接,引发级联超时与服务雪崩 。深层诱因:V1 版本 syncOrderDeliveryInfoByDelivery@Transactional 事务内同步调用顺丰医寄通 API和其他业务处理(handleDelivered,约 20s),使单连接被长时间占用并持有 ihm_order_prescription 行锁,显著放大了小连接池的脆弱性。syncOrderDeliveryInfoV2 已将物流下单移出事务与锁,仅单医院试点,非本次事故主因
关键量化 MySQL max_connections=4190,当前 Threads_connected=387;而按现有 12 服务 × 2 实例、多数服务仅 10 连接估算,全集群最大连接数仅约 160~560,远低于 MySQL 容量,连接池是瓶颈而非数据库
MySQL 侧状态 Threads_connected=387max_connections=4190,MySQL 总体连接数未耗尽,但单服务连接池已打满

二、问题现象详细描述

2.1 业务侧表现

  • 09:05 起,租户回调、HIS 处方同步、发药、查询等接口开始出现大面积超时。
  • 以 V1 入口 TenantCallbackServiceImpl.java#L147(file:///e:/javaProject/chinaunicom-medical-ihm/chinaunicom-medical-ihm-business/chinaunicom-medical-ihm-portal/src/main/java/com/chinaunicom/medical/ihm/service/impl/TenantCallbackServiceImpl.java#L147) orderApi.syncOrderDeliveryInfo(dto).getData() 为例:该接口在 Patient 服务由 OrderServiceImpl.syncOrderDeliveryInfoByDelivery@Transactional 大事务执行。
  • V1 与 V2 的核心差异在于顺丰 API 调用是否在事务内
    • V1:OrderServiceImpl.syncOrderDeliveryInfoByDelivery 方法加 @Transactional(rollbackFor = Throwable.class),在事务内同步调用 rpDeliveryService.handleDelivered(logisticsCompanyEnum, rpDeliveryData)(顺丰医寄通下单,约 20s),随后才更新 ihm_order_prescriptionihm_orderhis_order 等表。整个过程中数据库连接与行锁被持续占用。
    • V2:SyncDeliveryInfoV2ServiceImpl.syncByDelivery 先把顺丰 API 调用放在事务与 Redisson 锁之外 执行,仅将处方、订单、his_order 的更新通过 TransactionTemplate 包裹为短事务;锁也只覆盖 DB 写入段。顺丰 API 仍需同步调用,因为 HIS 需要实时获取返回的物流单号、备注等信息,不能异步化。
  • syncOrderDeliveryInfoV2 仅对单一试点医院放开,早晨仅有几笔订单,其他医院全部走 V1 入口,因此 V2 不是本次事故主因。
  • 多服务接口返回 网络繁忙,请稍候重试系统异常 等错误。

2.2 技术侧表现

1、连接池层(最直接) :核心服务(patient/portal/operate 等)使用 HikariCP 默认最大连接数 10仅hospital、mpc单独增加了连接数】,早高峰并发下迅速耗尽;新请求获取连接等待 5 分钟后失败,是服务大面积不可用的直接原因。

2、应用层 :大量 org.mybatis.spring.MyBatisSystemException: null,说明 MyBatis 无法获取 JDBC 连接。

3、MySQL 层 :大量 Lock wait timeout exceeded; try restarting transaction,集中在 ihm_order_prescription 表;这是长事务持有行锁后的次生现象,进一步加剧连接池耗尽。


三、日志分析关键发现

3.1 Lock wait timeout 日志分析

日志文件e:\javaProject\chinaunicom-medical-ihm\doc\Lock wait timeout日志.txt

3.1.1 发生频率
时间段 次数
09:15 57
09:16 51
09:17 38
09:18 38
09:19 50
09:20 55
09:21 24
合计 314

314 次锁等待超时集中爆发在 09:15 ~ 09:21,与系统整体超时时段完全吻合。

3.1.2 涉及的数据库操作
SQL 模式 次数 占比 说明
SELECT ... FROM ihm_order_prescription WHERE deleted=0 AND order_seq = ? 243 77.4% 按订单号查询处方全字段
其他 ihm_order_prescription 查询 47 15.0% apply_order_id 等条件
未知/上下文不完整 21 6.7% ---
合计 314 100% ---

关键结论 :所有锁等待全部集中在 ihm_order_prescription,说明该表存在严重的并发访问冲突。

3.1.3 典型日志片段
typescript 复制代码
2026-07-27 09:21:40.615 [patient,...] [http-nio-8883-exec-24] - [ERROR]
  [c.c.m.i.service.impl.ConsultationRecordServiceImpl : 1100] -
  org.springframework.dao.CannotAcquireLockException:
  ### Error querying database.
  Cause: com.mysql.cj.jdbc.exceptions.MySQLTransactionRollbackException:
  Lock wait timeout exceeded; try restarting transaction
  ### The error may exist in com/chinaunicom/medical/ihm/mapper/OrderPrescriptionMapper.java
  ### SQL: SELECT id,org_code,rp_seq,... FROM ihm_order_prescription
         WHERE deleted=0 AND (order_seq = ?)

分析

  • 即使是普通 SELECT 也出现了 Lock wait timeout,说明该查询在事务中执行,且存在其他事务长时间持有 ihm_order_prescription 相关行的锁。
  • 结合 innodb_lock_wait_timeout 默认 50s,意味着大量线程被阻塞近 50 秒后才报错,严重占用 Tomcat 线程和 Hikari 连接。

3.2 HikariPool/连接池相关日志分析

日志文件e:\javaProject\chinaunicom-medical-ihm\doc\0727系统超时问题-怀疑是HikariPool连接池被打满了.txt

3.2.1 异常类型分布
异常类型 特征 时间段
org.mybatis.spring.MyBatisSystemException: null 获取 JDBC 连接失败 09:05 ~ 09:23
3.2.2 时间分布
时间段 MyBatisSystemException 次数
09:05 13
09:06 12
09:07 7
09:09 12
09:10 12
09:11 24
09:12 13
09:13 20
09:14 13
09:15 31
09:16 31
09:17 31
09:18 34
09:19 7
09:22 11
09:23 23

异常从 09:05 开始持续,09:15-09:18 达到峰值,与 Lock wait timeout 趋势一致。

3.2.3 典型日志片段
typescript 复制代码
2026-07-27 09:23:51.909 [portal,...] [http-nio-8881-exec-11] - [WARN]
  [c.c.m.i.s.impl.SystemInteractionLogServiceImpl : 211] - 错误参数:
  org.mybatis.spring.MyBatisSystemException
    at org.mybatis.spring.MyBatisExceptionTranslator.translateExceptionIfPossible(...)
    at org.mybatis.spring.SqlSessionTemplate$SqlSessionInterceptor.invoke(SqlSessionTemplate.java:441)
    at jdk.proxy2/jdk.proxy2.$Proxy142.insert(Unknown Source)
    at org.mybatis.spring.SqlSessionTemplate.insert(SqlSessionTemplate.java:272)
    at com.baomidou.mybatisplus.core.override.MybatisMapperMethod.execute(...)
    ...

分析

  • 异常消息为 null,说明底层异常(如 Connection is not available, request timed out after 300000ms)在全局异常处理中被吞掉,仅保留了外层 MyBatisSystemException。经过详细排查,最终找到了连接池问题的关键证据!!

typescript 复制代码
message: ("Connection pool exhausted" OR "Cannot get connection" OR "too many connections" OR "Could not get JDBC Connection" OR "unable to acquire" OR "HikariPool" OR "Druid")
  • 但从堆栈可确认:所有异常均发生在 MyBatis 尝试从 HikariCP 获取连接/执行 SQL 时
  • 保存系统交互日志、获取预问诊信息等非核心业务接口也频繁报错,说明连接池问题已扩散到全服务。

3.3 MySQL 连接数量信息分析

数据来源e:\javaProject\chinaunicom-medical-ihm\doc\生产mysql连接数量信息.txt

sql 复制代码
-- 查看当前所有连接
SHOW FULL PROCESSLIST;

-- 查看当前连接数
SHOW STATUS LIKE 'Threads_connected';

-- 查看最大连接数限制
SHOW VARIABLES LIKE 'max_connections';

-- 按用户/主机分组统计连接数
SELECT user, host, COUNT(*) AS conn_count
FROM information_schema.processlist
GROUP BY user, host;


-- 查看portal写入的表是否有锁等待
SELECT * FROM information_schema.innodb_trx ORDER BY trx_started ASC;

-- 查看是否有慢查询
SELECT id, user, host, db, command, time, state, info
FROM information_schema.processlist
WHERE command != 'Sleep' AND time > 2
ORDER BY time DESC;
指标 数值 说明
Threads_connected 387 当前已连接会话数
max_connections 4190 MySQL 最大允许连接数
连接使用率 9.2% 387 / 4190

按主机统计的连接分布

主机 连接数
132.83.xx.91 3
132.83.xx.160 3
132.83.xx.133 3
其他 378

关键发现

  • MySQL 实例层面连接数远未耗尽(仅使用 9.2%)。
  • 这说明问题不在于 MySQL 总连接数不足,而在于单个 Java 服务的 HikariCP 连接池容量太小,无法充分利用 MySQL 的连接能力。

3.4 Nacos 配置分析

配置目录e:\javaProject\chinaunicom-medical-ihm\doc\nacos_config_ihm\ihm\

配置文件 是否配置 hikari maximum-pool-size 备注
common-mysql.yml YES default(10) 仅配置 connection-init-sql
hospital.yml YES 50 唯一显式配置较大的服务
mpc-server.yml YES 50 唯一显式配置较大的服务
patient.yml NO default(10) 核心服务,使用默认
portal.yml NO default(10) 核心服务,使用默认
operate.yml NO default(10) 核心服务,使用默认
baseservice.yml NO default(10) 基础服务
dict.yml NO default(10) 字典服务
orders.yml NO default(10) 订单服务
his.yml NO default(10) HIS 服务
consumer.yml NO default(10) 消费者服务

关键配置片段(hospital.yml)

yaml 复制代码
spring:
  datasource:
    hikari:
      connection-init-sql: set names utf8mb4
      minimum-idle: 25
      idle-timeout: 18000
      maximum-pool-size: 50        # 仅 hospital/mpc 显式配置
      auto-commit: true
      pool-name: hospital-db
      max-lifetime: 28800000
      connection-timeout: 300000   # 5 分钟

common-mysql.yml

yaml 复制代码
spring:
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://dbmaster:3306/...
    hikari:
      connection-init-sql: set names utf8mb4
      # 未配置 maximum-pool-size,默认 10

关键发现

  • 只有 hospitalmpc-server 两个服务显式配置了 maximum-pool-size=50
  • 核心高并发服务 patientportaloperate全部使用 HikariCP 默认最大连接数 10
  • connection-timeout=300000(5 分钟)设置过长,意味着当连接池耗尽时,线程会阻塞长达 5 分钟才失败,进一步加剧连接池耗尽。

3.5 连接池容量与并发能力测算

3.5.1 全集群连接池理论容量

按生产部署 12 个服务 × 2 实例( hospital/hospital_hd、patient/patient_hd、portal/portal_hd、operate/operate_hd、mpc/mpc_hd、baseservice/baseservice_hd ),结合 Nacos 配置估算:

服务 实例数 单实例 maximum-pool-size 该服务总连接
hospital + hospital_hd 2 + 2 50 ~200
mpc + mpc_hd 2 + 2 50 ~200
patient + patient_hd 2 + 2 10 (default) 40
portal + portal_hd 2 + 2 10 (default) 40
operate + operate_hd 2 + 2 10 (default) 40
baseservice + baseservice_hd 2 + 2 10 (default) 40
合计(理论峰值) --- --- ~560

注:即使按最乐观估算,全集群理论峰值也仅约 560 个连接 ,仅占 MySQL max_connections=419013.4% ;而事发时 Threads_connected=387,说明 MySQL 整体连接数远未耗尽,瓶颈在应用层单服务连接池

3.5.2 单服务并发能力估算

以 V1 syncOrderDeliveryInfoByDelivery 为例,假设顺丰 API 调用 + 事务内 DB 操作平均占用连接 20 秒

单服务连接池大小 理论最大并发处理数(20s/请求) 每分钟可处理请求数
10 10 30
50 50 150
100 100 300

关键结论

  • 当前 patient/portal/operate 等核心服务仅 10 个连接 ,在 20s 长事务下每分钟只能处理约 30 笔 发药/物流同步请求,早高峰极易被打满。
  • 若将连接池扩大到 50 ,同等条件下并发处理能力可提升 5 倍,能充分利用 MySQL 剩余的连接容量。
  • 若进一步将 V1 大事务改造为 V2 短事务(连接占用降至 1~2 秒),同样 50 连接可支撑的吞吐量还能再提升一个数量级。

四、根因定位过程

4.1 事故链路还原

typescript 复制代码
09:05  早间业务高峰开始
       │
       ▼
09:15  大量并发请求命中 patient/portal/operate 服务
       │
       ├─ 绝大多数医院走 V1 入口 TenantCallbackServiceImpl#L147
       │   └─ OrderServiceImpl.syncOrderDeliveryInfoByDelivery 使用 @Transactional 大事务
       │       ├─ 事务内同步调用外部顺丰医寄通 API(handleDelivered,~20s)
       │       ├─ 事务内更新 ihm_order_prescription、ihm_order、his_order 等表
       │       └─ 长时间占用 Hikari 连接并持有 ihm_order_prescription 行锁
       │
       ├─ V2 入口仅单医院试点,订单量极少,且顺丰 API 已移出事务/锁,不是主因
       │
       ├─ 其他请求需要查询/更新 ihm_order_prescription
       │   └─ 由于长事务持有行锁,触发 Lock wait timeout(50s)
       │
       ▼
09:17  Hikari 连接池(10 个)被长事务与锁等待长时间占用
       │
       ▼
09:18  新请求无法获取连接 → MyBatisSystemException
       │
       ▼
09:19  Tomcat HTTP 线程池被占满(线程阻塞在获取连接或等待锁)
       │
       ▼
09:20  服务整体不可用,大量接口超时

4.2 根因分层

层级 根因 证据
直接原因(配置层) 核心服务 HikariCP maximum-pool-size 仅 10,单服务并发承载力极低;早高峰请求迅速耗尽连接池,是全服务雪崩的直接触发条件 Nacos 配置中只有 hospital/mpc 配置 50;全集群理论峰值仅 ~560,远低于 MySQL max_connections=4190
放大因子(事务层) V1 长事务在事务内同步调用顺丰 API+其他业务处理(约 20s),导致单连接被长时间占用并持有 ihm_order_prescription 行锁,使 10 连接的小池更快耗尽 V1 入口 OrderServiceImpl.syncOrderDeliveryInfoByDelivery 被 @Transactional 包裹,外部 API 与 DB 写入在同一事务
SQL 层 ihm_order_prescription 表并发访问冲突,锁等待严重 314 次 Lock wait timeout 全部集中在该表
超时层 connection-timeout=300000(5 分钟)过长,线程阻塞不释放 hospital.yml 配置;耗尽时线程阻塞 5 分钟才失败
观测层 异常 message 被吞,未打印底层 Connection is not available 日志中仅显示 MyBatisSystemException: null

4.3 关键矛盾点

  • MySQL 连接数充足 vs 应用连接池耗尽

    • MySQL Threads_connected=387max_connections=4190,使用率仅 9.2%。
    • 但单个 Java 服务 Hikari 连接池只有 10,无法横向扩展到 MySQL 的实际容量
    • 这是一种连接池配置未充分利用数据库能力的典型问题:数据库能扛 4190 连接,但应用层自己把并发能力限制在几十以内。
  • 不是 MySQL 整体撑不住,而是单个服务连接池太小

    • 按 V1 请求平均占用连接 20s 计算,10 连接的服务每分钟只能处理约 30 笔发药/物流同步请求,早高峰极易被打满。
    • 若将 patient/portal/operate 等核心入口服务池扩到 50,同等条件下并发承载能力提升 5 倍,可充分利用 MySQL 剩余容量。
    • 连接池扩容是缓解当前问题最直接、见效最快的手段。
  • 顺丰 API 不能异步,但 V2 已将其移出事务

    • 物流下单必须同步调用顺丰医寄通并立即返回物流单号给 HIS,因此不能改为 MQ 异步。
    • V2 的优化价值在于:把顺丰 API 调用放在事务与 Redisson 锁之外,使数据库连接和行锁只被短事务占用;V1 的问题在于 API 调用在事务内,放大连接与锁的占用时长。

4.4 V1 与 V2 版本责任划分

维度 V1 (OrderServiceImpl.syncOrderDeliveryInfoByDelivery) V2 (SyncDeliveryInfoV2ServiceImpl.syncByDelivery)
入口比例 绝大多数医院(早晨高峰主要流量) 仅单个试点医院,几笔订单
顺丰 API 调用 @Transactional 事务内同步调用 在事务与 Redisson 锁之外同步调用
事务范围 大事务:机构查询、租户配置、顺丰 API、处方/订单/HIS 更新全部在一个事务 短事务:仅处方/订单/HIS 更新通过 TransactionTemplate 包裹
锁范围 Redisson 锁包裹整个方法(含顺丰 API 调用) Redisson 锁仅包裹 DB 写入段
连接占用时长 数十秒(API 耗时 + DB 写入) 数秒以内(主要是短事务 DB 写入)
本次事故角色 主要诱因/放大因子:事务内同步调用顺丰 API 使单连接占用 ~20s,让小连接池更快耗尽并引发锁竞争 非主因:流量极小,且顺丰 API 已移出事务,不会长时间占连接

结论:V2 版本本身不是本次事故主因,反而是解决方向;真正的风险是绝大多数医院仍走 V1 大事务入口。


五、影响范围评估

5.1 受影响服务

服务 影响程度 说明
patient 🔴 严重 发药、处方同步、订单查询等核心业务大量超时/失败
portal 🔴 严重 租户回调、文件下载、日志保存等接口异常
operate 🟡 中等 运营平台接口受影响
hospital 🟢 较轻 因连接池 50,相对耐冲击,但仍可能受 MySQL 锁影响
mpc/baseservice 🟡 中等 取决于具体配置

5.2 受影响业务

业务场景 影响
HIS 处方同步 同步失败,可能数据延迟
处方发药/退药 物流下单超时,患者无法及时收到药品
订单查询 查询超时,用户体验差
系统交互日志 无法记录,影响审计与排查
预问诊信息查询 获取失败

5.3 数据一致性风险

  • Portal Feign 超时断开后,Patient 服务线程仍在继续执行 并完成物流下单,可能导致:
    • 上游认为失败,下游实际成功(状态不一致)。
    • 重试时可能触发重复发货(需依赖幂等校验)。

六、解决方案建议

6.1 紧急止损(已发生或再次发生)

  1. 临时扩容连接池(最高优先级)

    • 将 patient、portal、operate 等核心服务的 maximum-pool-size 临时调整到 50~100
    • 同时调小 connection-timeout3000~5000ms,快速失败避免线程长期阻塞。
    • 预期效果:按 V1 请求平均占连接 20s 估算,单服务并发承载能力从约 30 req/min 提升到 150~300 req/min,可直接利用 MySQL 剩余的连接容量,是止血最快手段。
  2. 重启/扩容受影响的 Pod

    • 当连接池已耗尽时,重启可快速释放被占用的连接和线程。
    • 增加 Pod 副本数可临时提升整体连接数。
  3. 限流降级

    • syncOrderDeliveryInfosyncHisOrder 等重接口进行限流(如 Sentinel),降低并发峰值对连接池的冲击。
    • 非核心接口(如系统交互日志保存)可异步化或丢弃。

6.2 短期优化(1-2 周内)

  1. 统一 HikariCP 配置,充分使用 MySQL 并发能力

    common-mysql.yml 中统一配置合理参数,让所有服务默认获得足够连接,避免个别服务成为瓶颈【这里 maximum-pool-size连接池大小先设置50,后续多观察,再进行调整】:

    yaml 复制代码
    spring:
      datasource:
        hikari:
          connection-init-sql: set names utf8mb4
          # --- 核心连接池配置 ---
          # 最小空闲连接:闲时维持 25 个。
          # 作用:保证早晚高峰切换瞬间有足够的"热"连接可用,无需从 0 开始创建,同时避免闲时过多连接占用 DB 资源。
          minimum-idle: 25
          # 最大连接池大小:忙时扩容至 50 个。
          # 作用:应对早高峰突发流量,直接将单服务承载力提升 5 倍(相比默认 10)。
          maximum-pool-size: 50
          # --- 关键超时配置 ---
          # 数据库连接超时时间,默认30秒,即30000毫秒。
          connection-timeout: 30000 
          # --- 连接生命周期管理 ---
          # 空闲超时:10 分钟。
          # 优化理由:当流量从高峰回落后,连接会在空闲 10 分钟后逐渐回收至 minimum-idle (25)。
          # 这个时间窗口设置得稍长一点(10分钟),可以避免流量波动造成的频繁连接创建/销毁(抖动)。
          idle-timeout: 600000
          # 连接最大生命周期:30 分钟。
          # 作用:防止长时间复用同一个连接导致 MySQL 端状态不一致或内存泄漏。
          max-lifetime: 1800000
          # --- 稳定性保障 ---
          # 连接泄漏检测:60 秒。
          # 作用:针对长事务问题,如果一个连接被占用超过 60 秒,HikariCP 会打印警告日志(Close connection exception...)这有助于我们在生产环境快速发现那些未及时释放的长事务代码。
          leak-detection-threshold: 60000
          # 其他基础配置:此属性控制从池返回的连接的默认自动提交行为,默认值:true
          auto-commit: true
          pool-name: HikariCP-Common

    各服务如需更大容量,可单独覆盖:

    yaml 复制代码
    spring:
      datasource:
        hikari:
          maximum-pool-size: 100
  2. 缩短 MySQL 锁等待超时

    • 设置 innodb_lock_wait_timeout=10~20(当前默认 50s),让锁竞争快速失败,避免线程长期占用。
  3. 优化 ihm_order_prescription 表访问

    • 检查 (order_seq, deleted) 索引是否合理。
    • 避免大字段全量查询,减少锁持有时间。
    • 将非必要查询移出事务。
  4. 连接池更新配置生效,效果如下:

6.3 中期改造(1 个月内)

  1. 扩大 V2 覆盖范围,逐步下线 V1

    • SyncDeliveryInfoV2ServiceImpl 已将顺丰 API 调用移出事务与锁,并使用 TransactionTemplate 短事务写入 DB。
    • 在确认 V2 业务等价性后,逐步将更多医院切到 V2(通过 ihm_hospital_config.sync_delivery_info_version=2),从入口上消除 V1 大事务。
    • 顺丰 API 仍需同步调用(HIS 需实时获取物流单号),不能改为 MQ 异步。
  2. 对 V1 做最小化改造(过渡方案)

    • 若部分医院短期内无法切 V2,可在 V1 的 OrderServiceImpl.syncOrderDeliveryInfoByDelivery 中:
      • rpDeliveryService.handleDelivered(...) 提前到事务开启前执行;
      • 或引入 TransactionTemplate 仅包裹处方/订单/HIS 更新段,缩小事务范围。
  3. 全局异常处理优化

    • GlobalExceptionHandler 中正确打印异常 cause,避免 MyBatisSystemException: null 无法定位根因。
  4. 接口超时配置梳理

    • Feign readTimeout 从 180s 调整至合理值(如 30s),配合熔断降级。
    • Tomcat 线程池、HTTP 超时同步评估。

6.4 长期建设

  1. 连接池监控告警

    • 接入 HikariCP Micrometer metrics,监控 activeConnectionspendingThreadstotalConnections
    • 设置告警:pendingThreads > 5 或 activeConnections/max > 0.8。
  2. 数据库锁监控

    • 在 MySQL 中持续监控 information_schema.INNODB_TRXperformance_schema.data_lock_waits
    • 对长事务(> 10s)实时告警。
  3. 压测与容量规划

    • 定期开展全链路压测,验证连接池、线程池、MySQL 锁的承载能力。
    • 建立容量基线文档。

七、预防措施

  1. 禁止核心服务使用默认连接池配置 :所有服务的 maximum-pool-sizeconnection-timeoutleak-detection-threshold 必须显式配置并经过评审;配置需与 MySQL max_connections 做容量匹配,避免应用层人为限制数据库并发能力。
  2. 外部 API 调用禁止放在事务内:物流、支付等外部依赖必须在事务边界外调用。
  3. 长事务治理:任何超过 5 秒的事务必须拆解或异步化。
  4. 异常信息完整输出:禁止在全局异常处理中吞掉 cause,确保问题可定位。
  5. 常态化监控:HikariCP、MySQL 锁、Tomcat 线程池必须接入监控大盘。

八、结论

本次 07-27 早间接口超时事故的本质是 "应用层 HikariCP 连接池容量过小 + V1 长事务放大连接占用" 引发的级联雪崩,而非 MySQL 整体连接数不足。

  • 最直接原因:核心服务 HikariCP 仅 10 个连接,在 V1 长事务占连接约 20s 的场景下,单服务每分钟仅能处理约 30 笔发药/物流同步请求,早高峰迅速耗尽连接池,是全服务不可用的第一块多米诺骨牌。
  • MySQL 总连接数充足(387/4190),全集群理论峰值也仅约 560,远低于 MySQL 容量;瓶颈在应用层连接池配置,而非数据库。
  • ihm_order_prescription 表锁竞争严重,314 次 Lock wait timeout 均集中于此,是长事务持有行锁后的次生现象。
  • V1 大事务在事务内同步调用顺丰 API,长时间占用连接并持有行锁,显著放大了小连接池的脆弱性;V2 已将该 API 移出事务,不是主因。
  • connection-timeout=300000 过长,连接池耗尽时线程阻塞 5 分钟才失败,进一步拖垮 Tomcat 线程池。

最优先的改进措施

  1. 立即在 Nacos common-mysql.yml 中统一 HikariCP 配置,核心服务 maximum-pool-size=50(可按需 100)、connection-timeout=5000ms:这是缓解当前问题最直接、见效最快的手段,可让单服务并发承载能力提升 5~10 倍,充分利用 MySQL 剩余连接容量。
  2. 同步调小 innodb_lock_wait_timeout 至 10~20s:让锁竞争快速失败,避免线程长期占用连接。
  3. 尽快扩大 syncOrderDeliveryInfoV2 覆盖范围或改造 V1 事务边界 :把顺丰 API 调用移出 @Transactional 事务,仅对处方/订单/HIS 更新使用短事务;顺丰 API 因需实时返回物流单号给 HIS,不能异步化。

附录:关键数据图表

A. Lock wait timeout 时间分布

typescript 复制代码
09:15 ████████████████████████████████████████████████  57
09:16 ██████████████████████████████████████████████    51
09:17 ██████████████████████████████████████            38
09:18 ██████████████████████████████████████            38
09:19 ██████████████████████████████████████████████    50
09:20 ████████████████████████████████████████████████  55
09:21 ████████████████████████                          24

B. MyBatisSystemException 时间分布

typescript 复制代码
09:15 ████████████████████████████████████████████████  31
09:16 ████████████████████████████████████████████████  31
09:17 ████████████████████████████████████████████████  31
09:18 ██████████████████████████████████████████████████ 34
09:23 ██████████████████████████████████                23

C. 各服务 HikariCP 配置对比

typescript 复制代码
hospital     ████████████████████████████████████████████████████████████████  50
mpc-server   ████████████████████████████████████████████████████████████████  50
patient      ██████████  10 (default)
portal       ██████████  10 (default)
operate      ██████████  10 (default)
baseservice  ██████████  10 (default)
dict         ██████████  10 (default)
orders       ██████████  10 (default)
his          ██████████  10 (default)
consumer     ██████████  10 (default)
相关推荐
格兰芬多呼神护卫20 小时前
# Agent 企业评估方案深度分析:从“看答案”到持续质量闭环_码士教育
java·开发语言·jvm
万亿少女的梦16820 小时前
基于Spring Boot、Vue.js与MySQL的流浪猫狗救助系统设计与实现
vue.js·spring boot·mysql·javaweb·权限管理
豆角焖肉20 小时前
冒泡、快排、堆排的实现逻辑与选择思路
java·算法·排序算法·快排·冒泡·堆排
番茄炒鸡蛋加糖20 小时前
Spring 事务传播机制 & 事务失效场景
java·后端·spring
这就是佬们吗20 小时前
回溯算法三板斧---掌握「回溯三问」思考模板快速入门回溯
java·数据库·算法
qq_1715388520 小时前
深入浅出 MyBatis XML:从入门到精通
xml·java·mybatis
qq_1715388520 小时前
Spring TransactionSynchronizationManager:事务同步的幕后指挥官
java·后端·spring
万亿少女的梦16821 小时前
基于SSM、JSP与MySQL的秦皇岛旅游服务管理系统设计与实现
mysql·mybatis·ssm·jsp·spring mvc
SimonKing21 小时前
Spring Boot 集成 OnlyOffice,5 分钟搞定 Word/Excel 在线编辑
java·后端·程序员