资讯动态

Activiti核心表ACT_RU_EXECUTION解析:执行实例树与避坑指南

发布时间:2026/10/17 1:01:47 来源:尧图企业网站定制
跑流程跑得多了你会发现Activiti这套引擎设计得确实有点东西尤其是它的运行时表结构。你部署一个流程、发起一次审批数据库里瞬间多出好几张表的记录其中出镜率最高、最让人琢磨不透的大概率就是这张ACT_RU_EXECUTION。如果你是第一次接触Activiti看到这个名字可能有点懵“execution”到底是个什么东西它和流程实例、任务节点之间是什么关系别急这篇文章就把这张表彻底扒干净顺便把我在项目里踩过的坑也一并倒出来保证你看完能直接上手查数据、排问题。ACT_RU_EXECUTION属于Activiti运行时Runtime表族系列里编号第12张。它存的是“执行实例”也就是流程跑到哪一步、当前有哪些分支在并行、哪些子流程在被调用。可以这么说这张表是Activiti跑起来之后最核心的一张“动态地图”所有正在运行的流程实例和它们身上的枝枝叉叉都记录在这里。本文适合Activiti 5/6/7的实践者不管你是后端开发、运维还是刚接触工作流引擎的初学者这篇都值得收藏。1. 先搞清楚定位ACT_RU_EXECUTION到底管什么1.1 运行实例表族里的老大哥Activiti的表大致可以分成三类流程定义类以ACT_RE_开头、运行时表以ACT_RU_开头、历史表以ACT_HI_开头。运行时的表包括任务表ACT_RU_TASK、变量表ACT_RU_VARIABLE、身份关联表ACT_RU_IDENTITYLINK、作业表ACT_RU_JOB、定时作业表ACT_RU_TIMER_JOB等而ACT_RU_EXECUTION是这一族里最特殊的一张因为它承担了流程实例和执行树的双重职责。有一个理解上的关键点ACT_RU_EXECUTION里每一行不一定代表一个完整流程实例它还可能代表流程实例下面分裂出来的某个执行分支。流程实例本身也是一条“根执行实例”根执行实例和普通子执行实例共用这张表靠字段区分。这种设计是Activiti引擎能处理复杂并发和子流程的根本原因。很多人刚接触时会把流程实例IDPROC_INST_ID_和普通执行实例ID混淆。简单记一句话一个流程实例必然有一个根执行实例根执行实例的ID_就是PROC_INST_ID_但反过来ID_不等于流程实例ID因为还有无数子执行实例存在。后面我会用实际快照说明这一点。1.2 一个真实场景下的数据快照假设我起了一个简单的请假流程包含“填单→审批→结束”三个节点。流程发起后我去查ACT_RU_EXECUTION大概率会看到类似下面的记录ID_PROC_INST_ID_PARENT_ID_IS_ACTIVE_IS_CONCURRENT_IS_SCOPE_1000110001nulltruefalsetrue100021000110001truefalsefalse两行记录代表的是第一次流程实例启动后根执行实例10001是整个流程的容器它下面有一个子执行实例10002作为当前活动节点“填单”的执行载体。你可以把根执行实例理解成一个文件夹子执行实例是文件夹里正在处理的那张纸。这个模型在很多流程引擎里都有类似设计只是Activiti把两者放在同一张表里所以字段特别多、关系特别绕。这种“一个流程实例对应多个执行实例”的设计在Activiti里叫“执行树”。后面会看到一旦流程走到并行网关或者会签节点这棵树会长出很多分支数据会变得非常壮观。如果你只盯着ACT_RU_TASK看任务永远理解不了流程引擎内部到底怎么调度你必须先看懂这张执行实例表才能理解任务为什么挂在这个节点而不是那个节点。2. 逐字段拆解每个列都有它存在的理由2.1 身份类字段与业务键先看最基础的一批字段。ID_主键执行实例的唯一标识。在Activiti 5/6里是64位字符串通常由引擎的IdGenerator生成。根执行实例的ID_等于流程实例ID但注意普通子执行实例的ID_跟流程实例ID就没关系了。REV_乐观锁版本号。每次更新这条记录都会REV_ 1如果两个事务同时更新同一条执行记录后提交的那个会因为版本号对不上直接报OptimisticLockingException。这也是并发流程里最容易遇到的异常之一。PROC_INST_ID_流程实例ID标识这一行执行记录属于哪个流程实例。对于根执行实例ID_和PROC_INST_ID_相等对于子执行实例PROC_INST_ID_继承父执行实例的值指向同一个流程实例。BUSINESS_KEY_业务主键。发起流程时你可以通过runtimeService.startProcessInstanceByKey(processDefinitionKey, businessKey)传一个业务主键过来通常是业务表单的ID。这个字段非常实用它让你能从业务侧反查流程实例而不用记流程实例ID。需要注意Activiti默认并不会强制这个字段在表里唯一所以如果你希望通过业务键精确查询最好自己在业务流程里做好唯一性控制。2.2 树形结构字段PARENT_ID_和SUPER_EXEC_ID_这两个字段是理解执行树、子流程调用的关键也是最容易搞混的地方。PARENT_ID_指向父执行实例ID。根执行实例这个字段为null所有子执行实例通过这个字段挂到父执行实例下面形成一棵树。一个普通活动节点至少会有一个子执行实例指向根执行实例一旦并行分支产生每个分支都会作为子执行实例挂到父执行实例下。SUPER_EXEC_ID_指向“调用子流程的那个父执行实例”。它专门记录外部子流程call activity或者事件子流程的调用关系。当A流程调用B流程作为子流程时B流程的根执行实例的SUPER_EXEC_ID_会指向A流程里发起调用的那个子执行实例ID。换句话说PARENT_ID_是纵向的父子树SUPER_EXEC_ID_是横向的跨流程调用链。实际项目里排查子流程问题时一条SQL把孩子流程的SUPER_EXEC_ID_反查回父流程立刻就能看清是谁调用了谁。比如你有一个主流程叫“订单处理”里面通过callActivity调用了“库存锁定”子流程那么子流程这行记录的SUPER_EXEC_ID_指向的就是主流程里发起调用时的那条活动执行记录。2.3 状态字段IS_ACTIVE_、IS_CONCURRENT_、IS_SCOPE_、IS_EVENT_SCOPE_这几个布尔字段初看很绕其实它们各自管一件事IS_ACTIVE_标识这条执行实例是否处于活跃状态。只有活跃的执行实例才会真正推动流程往前走。当流程处于等待状态比如停在用户任务上等审批时对应的执行实例依然是活跃的但有些纯容器型的执行实例可能不活跃只负责承载子节点。IS_CONCURRENT_标识这个执行实例是否处于并发分支中。当流程走到并行网关分裂出多条分支时每条分支的执行实例这个字段会是true根执行实例和收敛前的父执行实例则是false。IS_SCOPE_标识这个执行实例是不是一个“作用域”。作用域可以理解为子流程、多实例活动等拥有独立变量空间的范围边界。根执行实例通常IS_SCOPE_true每个子流程的根执行实例也是true普通活动节点则是false。IS_EVENT_SCOPE_标识是否事件作用域主要用于事件子流程、边界事件等场景。比如你给一个用户任务挂了一个定时器边界事件引擎会创建一个事件作用域执行实例来等待触发。这个字段平时不常见但遇到“流程卡住不往下走”的问题时往往都是它搞的鬼。2.4 挂起与版本号字段SUSPENSION_STATE_挂起状态。1表示激活2表示挂起。流程实例可以被runtimeService.suspendProcessInstanceById挂起挂起后该实例下的所有执行记录这个字段都会变成2引擎不会再往这个实例上派发新任务。这里有个注意点只挂起流程定义和挂起流程实例是两回事前者影响后续新发起的流程后者只影响当前实例别弄混。CACHED_ENT_STATE_缓存实体状态属于引擎内部优化用的一个状态位通常不需要业务关心。ACT_ID_当前执行实例所在的节点ID对应BPMN XML里的activityid比如usertask1。对于根执行实例这个字段往往是null对于正在活动节点上的子执行实例这个字段指向当前节点的id。这也是排查“流程到底卡在哪个节点”最直接的字段。提示ACT_ID_是节点ID不是节点名称。你在数据库里看到ACT_ID_Task_1abc要去BPMN文件里找对应的id才能定位到中文名称别直接拿它展示给用户。3. 核心机制解密为什么这张表既是串又是树3.1 普通流程实例的一行记录抛开理论从头模拟一遍最简单的串行流程。用Java API发起流程时ProcessInstance pi runtimeService.startProcessInstanceByKey(leaveProcess, LEAVE001);引擎干的第一件事是查流程定义然后往ACT_RU_EXECUTION插一行根执行实例。此时根执行实例ID_和PROC_INST_ID_相等PARENT_ID_nullIS_SCOPE_trueACT_ID_null。紧接着引擎要执行第一个节点如果第一个节点是开始事件那才真正创建当前的“活动执行实例”。根执行实例会把ACT_ID_指向startEvent然后进入下个节点到了用户任务节点时它会在根执行实例下再生成一条子执行实例PARENT_ID_指向根执行实例IS_ACTIVE_trueACT_ID_usertask1同时在ACT_RU_TASK里插入一条对应的待办任务。这里可以套用一个顺口溜根执行实例管全局子执行实例管节点任务表只跟节点走。后面排查问题先看执行实例树再看任务表顺序一清二楚。3.2 并发分支与多实例会签在表里长什么样当一个流程经过并行网关比如“审批通过后同时通知财务和人事”数据库里的执行树会瞬间多出两条并发子执行记录。根执行实例IS_ACTIVE_false下面挂了两个子执行实例它们的IS_CONCURRENT_true分别指向noticeFinance和noticeHR两个节点。两个分支本身互不干扰各自推进这就是并发执行的物理表现。等两个分支都到了并行网关的汇聚节点引擎才会把它俩合并子执行实例销毁父执行实例重新变为活跃。再看会签多实例活动。比如一个审批节点需要会签给3个人引擎会在节点上创建一条“多实例根执行实例”IS_SCOPE_true在这个作用域下面再生成3条子执行实例每条ID_不同但都挂在一个父执行下同时IS_CONCURRENT_true。这样每条子执行实例代表一个会签人员待办。有人通过或驳回时引擎根据配置的completionCondition去判断要不要提前结束。多实例这个场景最容易出问题的地方是你必须在表里能看到“一个父节点、多个子节点”的结构如果哪天发现只有一个子节点还在跑说明会签被提前终止了通常是因为完成条件表达式写得不对导致引擎认为“已满足3个人中的2个人同意”就结束了。3.3 子流程与SUPER_EXEC_ID_的调用链外部子流程call activity会额外牵扯到跨流程实例的关系。我画过一个主流程“采购申请”里面通过callActivity调用子流程“采购执行”。子流程发起后数据库里会出现一个独立的流程实例记录它的PROC_INST_ID_是新生成的跟主流程无关但它的SUPER_EXEC_ID_会指向主流程中发起调用的那条执行记录。排查问题的时候最典型的一个复杂场景是子流程已经删了主流程还挂在那边等子流程结果。如果你只查ACT_RU_TASK能看见子流程的待办但看不出它属于哪个主流程。想找到“发起子流程的那条主流程”就必须通过SUPER_EXEC_ID_反查ACT_RU_EXECUTION一路往上翻到根执行实例。-- 已知子流程实例ID反查父流程 SELECT e.PROC_INST_ID_ AS sub_process_id, pe.ID_ AS caller_execution_id, pe.PROC_INST_ID_ AS parent_process_id FROM ACT_RU_EXECUTION e LEFT JOIN ACT_RU_EXECUTION pe ON pe.ID_ e.SUPER_EXEC_ID_ WHERE e.ID_ 子流程根执行实例ID;注意子流程的根执行实例ID_和子流程的流程实例ID相等PROC_INST_ID_也是它自己。所以这条SQL能一次把调用链拉出来。在实际的系统里如果你需要做流程追踪图这个查询就是最核心的一环。3.4 execution和task别搞混聊到这里必须专门强调ACT_RU_EXECUTION和ACT_RU_TASK的区别。ACT_RU_TASK一行代表一个具体的待办任务包含办理人、任务名称、优先级、创建时间等。ACT_RU_EXECUTION一行代表一个执行实例表示流程执行到哪个节点、当前引擎状态如何。在用户任务节点两者通常一一对应一个活跃的子执行实例对应一个待办任务。但在等待事件节点比如接收任务、定时器边界事件上只有执行实例没有任务。这也是为什么“流程没任务了但流程实例还活着”是完全正常的现象尤其是有异步信号或定时器场景。还有一个容易踩的坑有时候你会看到ACT_RU_TASK里有任务但它的EXECUTION_ID_对应的执行实例在ACT_RU_EXECUTION里查不到。这种情况多半发生在任务已经被完成但历史还没刷新的极端窗口期或者数据已经被手工清理过。遇到这种脏数据别慌先看ACT_HI_TASKINST历史表有没有归档记录再判断是引擎异常还是人为删库造成的。4. 我们怎么用SQL“盘”这张表4.1 快速查当前有哪些流程在跑最简单的诉求我想知道系统当前有哪些流程实例还在运行。别去查ACT_RU_TASK因为等待事件节点没有任务任务维度会漏数据。正确姿势是把ACT_RU_EXECUTION里所有根执行实例拉出来SELECT e.ID_ AS execution_id, e.PROC_INST_ID_, e.BUSINESS_KEY_, e.PROC_DEF_ID_, d.KEY_ AS process_key, d.NAME_ AS process_name, e.START_ACT_ID_ AS start_activity, e.ACT_ID_ AS current_activity, e.SUSPENSION_STATE_ FROM ACT_RU_EXECUTION e LEFT JOIN ACT_RE_PROCDEF d ON d.ID_ e.PROC_DEF_ID_ WHERE e.PARENT_ID_ IS NULL ORDER BY e.START_TIME_ DESC;这里加LEFT JOIN ACT_RE_PROCDEF是为了拿到流程定义的业务key和名称。很多项目里流程定义ID_是带版本号的直接用这个ID去页面展示不友好一定要关联定义表。提示不同Activiti版本的字段名略有差异。Activiti 5里根执行实例有START_TIME_字段Activiti 6/7里可能没有如果字段不存在根据你实际库结构调整别照抄作业。4.2 查每个流程实例卡在哪个节点日常运维问得最多的是“你这个单子走到哪了”。如果你有业务主键BUSINESS_KEY_可以直接用它定位到流程实例再通过执行实例表快速找到当前节点SELECT e.PROC_INST_ID_, e.ACT_ID_ AS current_activity_id, d.KEY_ AS process_key, t.ID_ AS task_id, t.NAME_ AS task_name, t.ASSIGNEE_ AS current_assignee, t.CREATE_TIME_ FROM ACT_RU_EXECUTION e LEFT JOIN ACT_RE_PROCDEF d ON d.ID_ e.PROC_DEF_ID_ LEFT JOIN ACT_RU_TASK t ON t.EXECUTION_ID_ e.ID_ WHERE e.BUSINESS_KEY_ LEAVE001 AND e.IS_ACTIVE_ true;这条SQL有个细节我加上了t.EXECUTION_ID_ e.ID_而不是t.PROC_INST_ID_ e.PROC_INST_ID_。因为同一个流程实例下有多个任务时会签或并行用PROC_INST_ID_关联会把任务全拉出来而往往我们只关心当前活跃节点对应的任务。如果某条执行实例IS_ACTIVE_true但ACT_RU_TASK里没任务那说明它停在事件等待节点上。这时候要去ACT_RU_EVENT_SUBSCR事件订阅表或者ACT_RU_TIMER_JOB定时器任务表看有没有对应的订阅记录别误判成“任务丢了”。4.3 关联业务表与ACT_HI_表做全链路流程上跑完的业务将来肯定要查历史。最合适的方式是把ACT_HI_PROCINST历史流程实例表和ACT_HI_TASKINST历史任务表联起来而不是一直盯着运行时表。但如果你想做“运行中的流程实例监控大屏”那ACT_RU_EXECUTION和业务表关联是必须的。比如我要查“所有还在运行的采购申请并且把申请单号、申请人、金额带出来”可以这么写SELECT p.bill_no, p.applicant, p.amount, e.PROC_INST_ID_, e.ACT_ID_ AS current_node_id, d.KEY_ AS process_key FROM biz_purchase_order p INNER JOIN ACT_RU_EXECUTION e ON e.BUSINESS_KEY_ p.order_no LEFT JOIN ACT_RE_PROCDEF d ON d.ID_ e.PROC_DEF_ID_ WHERE e.PARENT_ID_ IS NULL;前提是发起流程时设置了BUSINESS_KEY_为业务单号。如果当初没有设置业务键那你只能通过流程变量去反查效率会差很多。这给所有初学者的一个建议是生产环境发起流程务必传businessKey并把业务主键和流程实例ID的映射关系落到业务表里否则后期做列表查询会非常痛苦。4.4 数据清理与恢复的底线运行时表本质上是“短期状态表”流程一旦结束这些记录就该被清掉。Activiti默认的配置里流程实例结束后ACT_RU_EXECUTION中的根执行实例和所有子执行实例都会被删除历史数据归档到ACT_HI_*表。但实际操作中经常会有流程实例因为异常、被动取消而残留数据。如果你确认这些数据是垃圾正确的做法是调用APIruntimeService.deleteProcessInstance(processInstanceId, 业务取消);而不是直接DELETE FROM ACT_RU_EXECUTION WHERE ...。因为运行时表之间有关联关系直接删执行实例而不删任务、变量、事件订阅会造成更多脏数据严重时能让你整个工作流引擎不可用。如果只是想临时清理测试环境的脏数据建议先备份然后按顺序删除关联表记录。顺序一般是ACT_RU_TASK→ACT_RU_VARIABLE→ACT_RU_IDENTITYLINK→ACT_RU_EVENT_SUBSCR→ACT_RU_TIMER_JOB/ACT_RU_JOB→ACT_RU_EXECUTION最后再清理ACT_HI_*对应的历史记录。任何情况下都不要只删执行实例表。5. 常见问题与避坑实录5.1 流程结束但ACT_RU_EXECUTION里还有脏数据这是我在项目里被问得最多的问题。某一个流程实例明明已经走完了查ACT_HI_PROCINST能看到END_TIME_有值但ACT_RU_EXECUTION里却还留着根执行实例记录导致“流程最单列表”里一直显示未结束。这种问题通常由几个原因造成引擎配置里设置了async executor流程结束时异步命令还没处理完数据短暂残留。等异步任务跑完自然会消失。流程中存在事件子流程或信号边界事件事件作用域执行实例没被正确清理。流程被强制删除时API调用出错或者直接手工操作数据库破坏了完整性。排查办法很简单看ACT_RU_JOB或ACT_RU_TIMER_JOB里有没有对应流程实例的任务在等待执行如果有等它跑完如果没有检查IS_EVENT_SCOPE_true的记录是否存在。最终判断确实属于残留再通过API清理。5.2 事件子流程的ACT_ID_为空不是数据丢了有一次我在排查一个“流程没反应”的问题打开ACT_RU_EXECUTION一看根执行实例正常子执行实例也都在但其中有一条子执行实例的ACT_ID_是null。当时第一反应是引擎写错了后来查了BPMN文件才发现那其实是事件子流程的起始事件它的执行实例在等待触发信号所以ACT_ID_暂时为空是正常的。这个经验告诉我们看到ACT_ID_为空时不要立刻判断是脏数据。先看同一行里IS_EVENT_SCOPE_是不是true如果是说明引擎在等一个事件去ACT_RU_EVENT_SUBSCR查一下这个执行实例有没有订阅记录就明白了。如果你看到事件订阅表里有记录但一直不触发那才是真正需要排查的问题比如信号没发出去、消息名称对不上或者消息从未被消费。5.3 看着像并发其实是父子关系没理解还有一次帮同事排查他说“为什么一个流程实例在ACT_RU_TASK里出现了两条任务是不是并发发散了”我让他贴ACT_RU_EXECUTION的记录结果发现两条子执行实例的IS_CONCURRENT_全是falsePARENT_ID_指向同一个根执行实例。这种结构说明不是并行网关而是存在嵌套的子流程或者多实例容器。区别在于并行分支的子执行实例IS_CONCURRENT_true而子流程作用域的子执行实例IS_SCOPE_true但内部并不一定并发。如果你要判断“当前是否处于多实例活动中”关键看父执行实例的IS_SCOPE_以及子执行实例的IS_CONCURRENT_组合。它们的组合含义整理如下表组合模式含义IS_SCOPE_trueIS_CONCURRENT_false作用域根比如流程实例根、子流程根IS_SCOPE_falseIS_CONCURRENT_true并发分支或多实例子节点IS_SCOPE_falseIS_CONCURRENT_false普通串行节点IS_SCOPE_trueIS_CONCURRENT_true多实例活动的根执行实例少数场景实际生产中遇到任务数量异常时把这个组合判断写在脑子里比瞎猜快得多。5.4 这张表别直接UPDATE不管多着急我都建议你不要直接执行UPDATE ACT_RU_EXECUTION SET ...。Activiti的运行时状态除了这张表还对应着引擎内存里的各种缓存、命令上下文。你手动改了数据库引擎内存里并不会同步更新随后它一执行异步任务或刷新状态数据就又会被覆盖回去。更严重的字段之间是有联动逻辑的比如你把SUSPENSION_STATE_改了但引擎内存里没有同步挂起标志后续任务派发还会照常发生流程会出现“看似挂起实则继续跑”的诡异状态。如果确实需要人工干预某个异常流程优先用官方API。runtimeService.suspendProcessInstanceById、runtimeService.activateProcessInstanceById、runtimeService.deleteProcessInstance、runtimeService.setProcessInstanceBusinessKey都是安全的入口。只有在数据库已经被搞坏、API根本跑不动的时候才考虑手工修表而且修之前必须备份、必须记录改动字段和影响范围修完一定要重启或清一下引擎的缓存。我自己在测试环境曾经手贱改过一次ACT_ID_想“跳过审批节点”结果引擎直接从内存状态里把节点重新拉回原处。后来的经验就一直是运行时表只读要改就找API要跳过节点就加网关条件别走歪路子。使用这张表的时间长了你会发现它其实是一个信息量极大的“流程运行快照”。只要理解根执行实例和子执行实例的树形结构理解IS_CONCURRENT_、IS_SCOPE_、SUPER_EXEC_ID_这三个核心字段绝大多数运行期问题都能通过一条SQL查出来。多动手做几次全链路查询你就能真正掌握Activiti设计者的思路排查问题也会越来越顺手。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑