Skip to content

子流程在创建大量数据的时候响应比较慢 #210

Description

@xlorne

场景说明

主流程节点 A-B-C-D-E,其中 C 节点为子流程节点。A 为开始节点,E 为结束节点,B 和 D 均为审批节点。
子流程节点 A-B-C-D,A 为开始节点,D 为结束节点,B 和 C 均为审批节点。

主流程 C 节点发起 6 个子流程(配置为"发起并提交"),每个子流程的 B 节点并签 20 个审批人(通过表单 approvers 字段传入),即一次性创建 6 × 20 = 120 条流程记录

为模拟真实环境,在流程操作人网关查询用户时增加 5ms 延时,检测主流程 C 节点提交的总体耗时,并定位优化空间。

本地复现与量化(单元测试)

新增复现测试 FlowIssue210SubProcessPerformanceTest:内存 mock 仓储 + 网关 5ms 延时,测得:

指标 数值
网关批量查询 findByIds 6 次(覆盖 120 人次)
网关单点查询 get() 15 次
网关总查询 21 次 ≈ 105ms
B/C 节点提交总耗时 ≈ 204ms

结论:本地 mock 仓储下,操作人网关(~100ms)约占一半,但这不是真实环境的主体。

真正慢的本质:不是网关,而是大量串行的 DB 往返

线上环境差异:数据库是达梦、走内网,本地单元测试用的是内存 mock 仓储(0 DB 往返)。监控显示操作人查询仅 ~5ms/次、共 ~100ms,但整体耗时约 10 秒,说明真正的瓶颈在记录持久化而非网关。

一次 C 节点批量提交(同一 @Transactional 内串行),粗算 SQL 往返:

操作 类型/数量
6 个子流程开始记录 INSERT × 6
6 × 20 条 B 节点并签审批记录 INSERT × 120
每个子流程 B 待办逐条查库 getByTodoKey N+1 SELECT × 120
待办记录 t_flow_todo_record INSERT × 120
并签合并 t_flow_todo_marge INSERT × 120

合计 ~500 条独立 SQL 往返,因受两点放大:

  • 主键 GenerationType.IDENTITY 使 Hibernate 无法 JDBC 批插(每条 INSERT 一次往返);
  • saveTodoMargeRecords 逐条 getByTodoKeyN+1

达梦 + 内网 + 每往返 15~25ms → ~500 × 20ms ≈ 10s,与线上反馈吻合。

优化实施(已合入 dev)

1. 高频热表主键 IDENTITY → SEQUENCE(原理:解锁 Hibernate 批插)

仅改 4 张高频写热表,低频流程设计表(t_flow_workflow/t_flow_workflow_version/t_flow_workflow_runtime)保持 IDENTITY 不动:

  • t_flow_recordt_flow_todo_recordt_flow_todo_marget_flow_sub_process_record
  • 示例配置开启 hibernate.jdbc.batch_size=50 + order_inserts + order_updates
  • 运行机制受影响面为零(id>0 判定、setId(entity.getId())fromId 引用、按 id 排序语义均不变)

2. 消除 N+1 ✔

FlowRecordSaveService.saveTodoMargeRecords 改为先分块批量加载已存在待办再循环处理:

  • 新增 FlowTodoRecordRepository.findByKeys(...),按 TODO_KEY_BATCH_SIZE=500 分块,避免海量 key 拼超大 IN 子句/结果集过大导致 OOM
  • N+1(每 20 条 20 次 SELECT)→ 每块 1 次 IN 查询
  • 新增断言验证:批量场景下逐条 getByTodoKey 调用数远小于待办数

removeTodoMergeRecords 对已办记录的逐条 getByTodoKey 清理路径因量级较小暂未改,可后续按需处理。)

遗留(需运维侧手动执行)

达梦存量库迁移ddl-auto=update 只新建序列对象,需手动对上述 4 张热表:

  • 去掉存量列的 IDENTITY 属性;
  • 建序列并把起始值播种到 MAX(id)+1(否则新插入主键冲突)。

验证

  • 全项目 ./mvnw clean install BUILD SUCCESS
  • framework 227 个测试通过
  • example 3 个 @SpringBootTest 集成测试(真实 JPA + H2)验证 SEQUENCE 与 findByKeys 实际生效

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions