尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

DolphinScheduler任务状态异常排查与僵尸任务修复实战指南

DolphinScheduler任务状态异常排查与僵尸任务修复实战指南 如果你们团队正在用 DolphinScheduler 跑生产调度那下面这个场景你一定不陌生某个工作日早上值班群里突然有人扔出一条消息——凌晨的任务全部卡在“运行中”工作流既不往下走也不报失败。打开 DolphinScheduler 的实例列表一大片任务永远停在 RUNNING像被人按了暂停键。日志里没有任何明显异常Worker 节点还活着数据库连接看起来也正常但任务就是一动不动。在 DolphinScheduler 的使用过程中任务状态异常可以说是最让人头疼的问题之一尤其是这种“僵尸任务”——数据库状态显示为运行中但底层实际的执行进程早就没了或者状态根本没有被正确回写。这类问题定位起来费时处理起来又不能乱来稍有不慎就会导致任务重复执行、数据重复写入比任务失败还可怕。这篇博文我打算把这几年处理 DolphinScheduler 任务状态异常的完整排查思路、核心表结构、修复 SQL、以及各种踩坑记录全部整理出来希望能帮你少走一些弯路。这篇文章适合谁如果你是 DolphinScheduler 的运维同学、大数据平台的开发或者正在从别的调度系统迁移到 DolphinScheduler、被各种任务状态问题折磨得焦头烂额的人这篇文章应该能帮到你。1. 灾难现场DolphinScheduler 任务状态异常是什么样1.1 三个真实场景帮你判断是不是遇到了同类问题先说最常见的三种表现你对照一下自己的现象是不是属于这一类。第一种是“全流程卡死”。某个工作流实例里第一个任务明明已经跑完了日志也显示成功退出但任务实例状态一直不更新后面的任务全部排队等待。这个现象比较典型说明状态写回链路在某一个环节断了。还有一种情况是“单个任务幽灵执行”——任务实例状态是 RUNNING但你去 Worker 机器上ps -ef | grep java看进程发现根本没有对应的执行线程或者 Python/Shell 子进程早就退出但状态一直没有被更新。第二种是“任务堆积”。因为前面的任务一直没有结束后续实例持续产生形成堆积。比如一个 5 分钟调度一次的任务如果某一个实例卡住了后面 50 个实例全部卡住或者排队数据库里t_ds_task_instance表大量 RUNNING 状态记录整个调度链路接近瘫痪。这种情况在多人共用一个集群的环境里尤其明显某个团队一个坏任务就能拖垮整个集群。第三种是“重启后状态丢失”。DolphinScheduler 服务重启之后部分原本运行中的任务直接变为“未知”状态工作流实例没法恢复需要人工介入才能让后续任务继续。这个通常和 Master/Worker 的状态恢复机制有关也和我们后面要讲的数据库状态不一致紧密相关。如果你看到的现象和上面任何一种对得上那基本可以确定是任务状态异常问题大概率涉及“僵尸任务”和数据库层的状态修复。接下来就要一步一步定位根因而不是盲目重启服务或者去手动改库。1.2 先分清问题边界调度问题、执行问题还是数据库问题处理任务状态异常最重要的一步是分清楚问题出在哪一层。我见过太多人一上来就去改数据库结果问题根源在 Worker 节点上改了也没用反而引入了新的数据问题。DolphinScheduler 的任务执行链路是API - Master - Worker - 数据库每一层都有可能出问题。如果是调度问题通常表现是 Master 日志里没有任务分配记录t_ds_command表里积压大量待消费的命令但 Master 没有把它们转成任务实例。这往往是 Master 卡死、负载过高或 ZooKeeper 会话超时导致的。如果是执行问题现象是 Master 已经分配了任务Worker 日志里也收到了任务但执行进程没有正常启动或者执行完了之后没有回调 Master。这种场景下日志一般会留下线索只是比较隐蔽。如果是数据库问题更常见的是状态根本写不进去或者写进去又变了——比如t_ds_task_instance的记录一直停留在提交状态没有任何更新。我习惯的排查顺序是这样的先看数据库里任务的最新状态和更新时间如果update_time已经是好几个小时之前了说明状态写回早就停了然后去 Worker 日志里搜对应的任务实例 ID看是不是收到了执行命令最后再看 Master 日志确认任务分配的情况。这样一层层往下走基本能把问题圈定在某一个环节里。2. 搞清楚状态机排查就不会瞎忙2.1 核心表结构任务状态在数据库里是怎么流转的DolphinScheduler 的元数据默认存储在 MySQL 或 PostgreSQL 里任务状态相关的最核心表是t_ds_process_instance工作流实例和t_ds_task_instance任务实例。这两张表的state字段保存了当前状态不同版本里字段名基本一致但状态值对应的枚举在不同版本之间可能有差别。举个例子3.x 版本里任务实例的state字段值为RUNNING_EXECUTION对应数字 4提交成功是SUCCESS对应数字 7失败是FAILURE对应数字 6被杀掉是KILL对应数字 8。不同版本的数字编码可能不一样所以做 SQL 修正之前最好先去代码里或者数据库注释里确认一下当前版本的状态枚举。这也是为什么我建议不要盲目照着网上一段 SQL 跑先确认版本的兼容性。工作流实例的状态流转链路是我排查的一个关键突破口每当一个任务执行完成Worker 会回调 MasterMaster 更新t_ds_task_instance的状态然后再更新对应的t_ds_process_instance状态判断是否触发后续任务。如果这个链路的某一环断了就会出现“任务实际已经完事但数据库状态不变”的僵尸现象。另外还有一张t_ds_command表它是调度命令的缓冲队列Master 不断从中拉取命令并创建实例。如果这张表里堆积了大量未消费命令也会造成状态异常。提示修复之前一定要先确认自己用的是哪个版本不同版本的state枚举值可能不同。我自己就吃过亏直接在 3.1.9 版本上跑 2.0 的修复 SQL差点把状态改乱了。2.2 定位关键日志的三个入口排查 DolphinScheduler 问题最简单直接的办法就是看日志。不同模块的日志记录了不同阶段的信息我一般按下面三个入口查Master 日志在master-server/logs/目录下搜索task instance id或者工作流实例 ID能看到任务分配、Task 状态回调的处理过程。Worker 日志在worker-server/logs/目录下搜索任务实例 ID 能看到任务执行、任务退出、状态上报的记录。API 日志在api-server/logs/目录下主要负责接收用户提交的请求和查询很多“页面看到的状态和数据库不一致”问题就是通过 API 层日志发现的。如果你是用 Docker 部署的比如基于dockerfile自己构建的 DolphinScheduler 镜像日志查看方式略有差异但思路是一样的docker logs只能看到容器启动和部分服务日志更完整的日志通常在挂载的日志目录里。我记得有个朋友用自建镜像跑生产任务卡死后排查半天最后发现 Worker 容器在凌晨重启过日志直接断档了容器重建导致任务状态全部残留为 RUNNING。这种环境问题用传统方式很难定位所以排查前先看看节点有没有重启过是最基本的一步。在实际排查中我喜欢把三个组件的日志放在同一时间轴上对比。比如某个任务 10:00:00 创建10:00:05 Worker 收到执行命令10:00:30 任务执行完毕但数据库状态一直停在 RUNNING。那么时间轴就清楚了10:00:30 之后的回调环节出了问题应该是 Worker 上报失败、Master 没有收到回调或回调处理异常。接下来针对性地去找 Master 日志里的异常堆栈就能定位到是网络超时还是代码异常了。2.3 一眼看出异常状态的 SQL 查询看日志之前我通常先用 SQL 快速捞一下当前异常状态的面貌这样对问题规模心里有数。-- 查询所有一直处于运行中的任务实例按项目维度看分布 SELECT project_code, process_instance_id, task_instance_id, task_name, state, start_time, end_time, update_time FROM t_ds_task_instance WHERE state 4 AND update_time NOW() - INTERVAL 30 MINUTE ORDER BY update_time ASC LIMIT 100;这条 SQL 的作用是把“运行中但长时间没有更新”的任务捞出来。正常情况下一个任务如果是运行中它的update_time会不断刷新尤其是长时间运行的任务会有心跳机制更新这个字段。如果update_time停留在 30 分钟甚至几个小时之前那基本可以断定状态写回链路出问题了。再查一下工作流实例-- 查询所有卡住的工作流实例及其包含的任务数量 SELECT pi.id, pi.name, pi.state, pi.start_time, pi.end_time, COUNT(ti.id) AS task_count, SUM(CASE WHEN ti.state 4 THEN 1 ELSE 0 END) AS running_task_count FROM t_ds_process_instance pi LEFT JOIN t_ds_task_instance ti ON ti.process_instance_id pi.id WHERE pi.state 4 GROUP BY pi.id ORDER BY pi.start_time DESC LIMIT 50;这两条 SQL 基本能帮你快速确认问题范围是零星几个任务卡住还是大面积工作流卡死。问题范围不同处理策略完全不同。零星任务卡住可能是单个 Worker 的问题大面积卡死则大概率是 Master 或数据库层面的问题。3. 僵尸任务是怎么产生的以及如何止损3.1 最常见的四种成因很多人以为僵尸任务就是“任务挂了但没有更新状态”实际上成因远比这个复杂。我梳理了几个常见的原因希望你能从中找到对应的场景。第一个是 Master 故障转移Failover导致的。在 HA 环境下如果 Master 节点重启或崩溃正在运行的工作流和任务实例可能会和新的 Master 之间出现状态同步延迟。旧 Master 上的任务如果还没有持久化到 ZooKeeper新 Master 恢复后会主动容错但如果容错失败任务就一直卡在 RUNNING。这种通常发生在集群变更之后。第二个是 Worker 节点宕机或失联。Worker 执行任务时会先写一个状态然后执行最后再写状态。如果 Worker 在任务执行过程中宕机数据库里的状态就停留在 RUNNING而实际进程已经没了。DolphinScheduler 有task group和容错机制尝试恢复但恢复失败时就会残留僵尸任务。第三个是数据库连接中断导致状态回写失败。Worker 执行完任务后回调 Master 更新数据库但刚好赶上数据库连接池耗尽、网络抖动或数据库锁等待状态更新失败且没有重试机制任务状态就停留在 RUNNING。这个在数据库负载高的场景下尤其常见。第四个是任务自身没有正常退出。前端脚本、Shell 命令里启动了后台进程主进程退出了但子进程还挂着或者任务里用了不规范的nohup导致 Worker 认为任务还在执行。这种情况下任务状态是 RUNNING但代码逻辑早就该结束了属于典型的伪活任务。我见过最离谱的是一个 Python 脚本里调了第三方接口接口超时时间是 30 分钟脚本自己却没有超时机制结果每个任务都挂到 30 分钟才结束调度系统被拖得满身是伤。3.2 止损第一件事让调度先停下来发现大面积僵尸任务后第一件事千万别急着去改数据库先止损。所谓止损最有效的方法是暂停调度让新的任务不要再产生避免僵尸任务像滚雪球一样越积越多。具体操作是停止 Master 节点的调度消费或者更直接一点暂停相关的定时任务。在 DolphinScheduler 的 Web UI 上可以把对应的工作流项目切换到“离线”状态这样定时调度会暂停Master 不再消费新的 Command。如果是整个集群都出问题了可以在数据库层面堵一下把t_ds_command表里的待消费命令先备份并清理或者直接停掉 Master 服务来阻止新任务分配。注意我这里说的是暂停调度不是停数据库。数据库是状态存储层轻易不要动除非你明确知道要做什么。止损之后还要做一件事确认当前集群里还有没有真正在跑的 Worker 进程。这一步非常关键因为如果还有 Worker 在跑你后面改数据库状态很容易被 Worker 的回写重新覆盖改了等于白改。我当时处理过一次很折磨人的问题——明明把任务状态改成失败了结果 Worker 进程收到任务执行完成的回调又把状态改回了成功两边互相打架页面状态不断跳变。后来我学乖了修复前先jps或者ps -ef | grep worker-server确认没有相关进程之后再做数据库修正。3.3 确认当前集群的健康状态止损之后花几分钟确认集群健康状态有助于判断修复方案的优先级。我一般看下面几个方面第一是节点状态。在 Web UI 的监控中心里看 Master、Worker 节点是否在线有没有节点处于“失联”或“下线”状态。如果用 Zookeeper 做注册中心也可以看 ZK 上注册的节点信息。第二是数据库连接池。DolphinScheduler 的各个组件对数据库依赖很高如果连接池配置太小并发高峰时很容易出现获取连接超时进而导致状态回写失败。第三是日志增长情况。如果master-server和worker-server日志还在持续输出且没有明显异常说明服务本身是活的问题可能出在具体的任务执行层面。对于容器化部署的场景还要额外看容器本身的状态。用dockerfile构建 DolphinScheduler 镜像时如果有一些环境变量、时区、JVM 参数没配好容器重启后会造成很多隐性问题。我之前遇到过容器因为 JVM 内存参数设置不合理在任务高峰时被 OOM Killer 杀掉容器自动拉起后所有任务状态都停留在 RUNNING排查了很久才找到原因。所以如果你们的 DolphinScheduler 跑在容器里先看看容器有没有自动重启过这个信息 10 秒钟就能确认却往往被忽略。4. 数据库修复全流程实操4.1 修复前的准备工作备份与快照如果前面的排查已经确认是数据库状态异常需要手动修正了那进入数据库操作前我强烈建议先做备份。不是说服务一定不能挂而是手动改数据这个动作本身有风险一旦改错回滚的成本远高于备份的成本。备分方式很简单如果 DolphinScheduler 用的是独立数据库实例直接对相关表做备份即可-- 备份核心表假设库名为 dolphinscheduler CREATE TABLE t_ds_task_instance_bak_20250101 LIKE t_ds_task_instance; INSERT INTO t_ds_task_instance_bak_20250101 SELECT * FROM t_ds_task_instance; CREATE TABLE t_ds_process_instance_bak_20250101 LIKE t_ds_process_instance; INSERT INTO t_ds_process_instance_bak_20250101 SELECT * FROM t_ds_process_instance; CREATE TABLE t_ds_command_bak_20250101 LIKE t_ds_command; INSERT INTO t_ds_command_bak_20250101 SELECT * FROM t_ds_command;如果你有权限且库不大直接mysqldump整个库也不是不行但注意业务高峰期可能会引起锁表所以能只备份相关表就只备份相关表。PostgreSQL 的话对应语法略有差异但思路一样建备份表、导入数据。这里有个很容易被忽略的细节备份不仅是给自己留后路更多是为了对比。修复之后如果状态又异常了你可以通过对比备份表和当前表快速确认是不是有进程在往回写状态。我处理过一个诡异问题修完状态后 10 分钟又变回 RUNNING后来就是通过对比备份表才发现原来还有个 Worker 进程根本没停掉它完成了任务之后把状态从“失败”重新写回了“成功”。4.2 状态修正的核心思路先子后父、先停后改数据库修复最怕的是“好心办坏事”。比如你把一个 RUNNING 状态的实例直接改成 SUCCESS但实际底层任务还没有跑完或者 Worker 还在执行中那就会造成任务没执行完却被标记成功下游任务以为数据已经准备好了结果拉到半成品数据酿成更大的事故。所以改数据库之前必须确认两件事第一该任务对应的 Worker 进程确实已经不在了第二任务不会因为状态修改而被重复调度执行。修复的顺序我总结成八个字先子后父、先停后改。先子后父的意思是先修t_ds_task_instance子表再修t_ds_process_instance父表。因为 Master 判断工作流是否完成是通过统计所有子任务的状态来决定的如果子表状态没有修正父表改了也没用甚至会导致流程状态错乱。先停后改的意思是修改前确保相关的 Master/Worker 节点处于停止状态或者至少已经确认没有相关任务在执行避免状态被回写覆盖。这里还有个容易踩的坑把任务状态从 RUNNING 直接改成 FAILED 还是 KILL完全取决于业务场景。如果你确定任务是被异常中断的且没有继续执行的必要改成 FAILED 更合理如果你是通过管理系统主动停止的任务改成 KILL 更合适。如果任务卡住但底层数据不涉及一致性你又希望工作流继续往下走也可以考虑先把失败的任务状态改成成功然后人工补数。但这个操作风险比较高大多数人不太建议在生产环境直接这么干。4.3 实操 SQL 与脚本一套可以直接抄的修复方案下面给出一套我在生产环境用过的基础修复 SQL你可以根据自己的版本调整状态枚举值。这里以 DolphinScheduler 3.x 版本为例假设state 4表示 RUNNING。先把状态从 RUNNING 改为失败FAILURE并把end_time更新为当前时间-- 1. 修正任务实例把长时间处于 RUNNING 且 Worker 已不存在的任务置为失败 UPDATE t_ds_task_instance SET state 6, end_time NOW(), update_time NOW() WHERE state 4 AND update_time NOW() - INTERVAL 30 MINUTE;如果你想把卡住的任务标记为“杀掉”而不是失败把state 6改成对应的 KILL 枚举值即可。修改完子表之后再更新父表——把已经没有“运行中”子任务的工作流实例状态改为失败-- 2. 修正工作流实例如果该流程实例下已经没有 RUNNING 的子任务则把父流程置为失败或停止 UPDATE t_ds_process_instance SET state 6, end_time NOW(), update_time NOW() WHERE state 4 AND id IN ( SELECT DISTINCT process_instance_id FROM t_ds_task_instance WHERE state 6 );注意这里有个细节一条更新语句里不能同时更新一个表并查询它的子查询结果MySQL 会报错所以如果遇到类似语法问题可以把子查询包一层临时表或者用EXISTS改写。处理完任务实例之后还要清理一下t_ds_command表里积压的待消费命令不然 Master 恢复后还会继续处理这些旧命令导致重复调度-- 3. 清理命令表中未消费且对应的流程已不存在的记录谨慎操作 DELETE FROM t_ds_command WHERE process_instance_id IN ( SELECT id FROM t_ds_process_instance WHERE state 6 AND end_time IS NOT NULL );如果你们的调度用了任务组Task Group机制可能还需要清理t_ds_task_group_queue里卡住的排队记录否则任务组的队列会被占满后续任务永远拿不到资源。修复 SQL 可以参考-- 4. 清理任务组队列中排队时间过长的记录按需使用 UPDATE t_ds_task_group_queue SET status 3, update_time NOW() WHERE status 2 AND create_time NOW() - INTERVAL 2 HOUR;status 2表示在队列中等待status 3表示放弃或已结束具体枚举值建议查对应版本的代码。注意这个表不是所有版本都存在如果你用的是 3.1.x 开启任务组功能的版本才需要处理。重要提醒以上 SQL 是我在 3.x 版本上验证过的但不同版本状态枚举值可能有变化。例如 3.1.9 和 3.2.0 之间的state字段含义基本一致但更早的 1.x、2.x 版本存在差异。执行前一定要先在测试环境验证或者至少先跑一条SELECT确认影响范围再跑UPDATE。4.4 修复完成后的验证流程数据库改完之后别急着立刻启动业务先按下面的顺序验证一遍。首先是检查剩余卡住的任务数量-- 验证是否还有残留的 RUNNING 任务 SELECT COUNT(*) FROM t_ds_task_instance WHERE state 4 AND update_time NOW() - INTERVAL 30 MINUTE;这个查询应该返回 0如果还有大量残留说明要么是修复 SQL 没覆盖到要么是还有 Worker 在持续产生新的 RUNNING 状态。第二步是确认表数据完整性看更新后的记录是否符合预期。第三步再启动调度服务先启动master-server再启动worker-server启动后不要急着跑业务任务先观察日志有没有异常等一个新的调度周期看能否正常生成和执行。验证阶段我还有一个习惯直接跑一条简单的测试任务比如一个只做echo hello的 shell 任务确认整套调度链路恢复了。这条测试任务执行成功后再逐步打开之前暂停的业务调度。如果一上来就直接恢复全量调度万一还有遗漏的问题很快就又会冒出来一些新的僵尸任务到时候定位难度反而更大了。5. 常见问题与排查技巧实录5.1 修完状态又变回 RUNNING为什么这个问题我前面提过这是修复过程中最常见的坑。原因很简单你以为状态是数据库里的状态但其实 Worker 或 Master 进程还在运行任务执行完之后的回调把状态改回去了。或者更隐蔽一点Master 上的容错机制扫描到任务没有正常结束主动把它从失败状态重新拉回 RUNNING 状态试图重新调度。我的处理经验是修改数据库之前必须确认相关节点上的任务执行进程已经不存在。怎么确认一种是看进程列表比如ps -ef | grep java看有没有 worker-server 在跑还有一种更精确的方式是通过 DolphinScheduler 的 API 或数据库查询看该任务实例对应的host字段然后去那台机器上确认进程状态。如果任务被分到了某个 Worker而那个 Worker 恰好失联了状态修复后也可能会因为 Master 的容错机制再次拉起。这种情况下你需要临时关闭该 Worker 或者调整容错策略才能让修复结果稳固下来。5.2 僵尸任务不断新增怎么查如果你把存量僵尸任务清完了过一会儿又出现新的说明问题不是存量数据而是持续有任务在执行过程中“卡死”。这类问题要重点看新增任务的规律是某一个固定的工作流产生的还是随机分布的是特定时间点出现还是持续出现。如果是某个固定工作流产生的去查这个工作流对应的脚本逻辑大概率是脚本里用了会挂起的外部调用或者资源等待。如果是随机分布且持续出现就要怀疑是 Worker 节点的问题了可能是某个 Worker 的 JVM 内存不足导致频繁 GC、线程池耗尽或者是该 Worker 节点上的数据库连接池配置太小状态回写经常失败。我当时处理过一次持续新增僵尸任务的情况最后发现是数据库里一张表的索引膨胀严重update_time字段上的索引失效UPDATE语句全表扫描导致状态回写极慢前端页面看着就是任务一直卡在 RUNNING。后来重新组织索引之后问题彻底消失。5.3 页面状态和数据库不一致是不是缓存有时候你查数据库发现任务状态已经是成功了但 DolphinScheduler 页面上还显示 RUNNING。这种情况通常不是数据库问题而是 API 层查询的时序问题——前端页面每隔十几秒才轮询一次数据库状态更新后短时间内页面没刷新。大多数人不用太担心等一个轮询周期就好了。但如果长时间不一致比如几分钟甚至几十分钟页面还是旧状态那就要查 API 层了。DolphinScheduler 有些版本对实例列表做了分页缓存或者查询走了只读从库主从之间有延迟。你可以在 API 日志里搜索这个任务实例 ID看查询接口返回的状态值到底是什么再和数据库比对就能定位到是哪个环节的缓存问题。还有一个办法直接调 API 接口传参数强制刷新或者清理浏览器缓存很多时候只是前端的显示问题。5.4 Docker 部署场景下的额外注意点现在很多团队用 Docker 部署 DolphinScheduler用dockerfile自建镜像的场景也不少见。容器化部署在扩展性和资源隔离上有优势但排查任务状态异常时会多一些特有的坑。第一个是插件依赖包问题。DolphinScheduler 的 Worker 执行各种类型任务Shell、Python、Seatunnel、Flink 等时依赖的插件和 JAR 包都需要放到指定目录。如果你用dockerfile构建镜像时只打了基础包没有把任务需要的插件依赖包全部打进去任务执行到一半就可能因为找不类而失败。如果失败回调没处理好状态就会停在 RUNNING看起来像僵尸任务实际是插件缺失。和 SeATunnel 集成时尤其常见——seatunnel任务需要对应的 connector 插件如果容器镜像里没有对应版本的 connector任务启动就会失败而且失败信息在日志里往往不那么显眼。第二个是容器重启导致的状态残留。容器因为内存不足或健康检查失败被自动重启后老容器上正在执行的任务状态永远不会被回写节点重新注册后也是一个“新”的 Worker旧任务的恢复只能靠 Master 的容错机制。如果容错机制没触发任务就留在 RUNNING页面上一片红。所以我遇到 Docker 部署的场景第一件事就是查容器的重启事件docker inspect container看RestartCount或者直接看容器创建时间是否早于任务卡住的时间几秒钟就能定位问题。第三点是挂载卷和日志的持久化。容器如果没挂载宿主机目录容器重建后日志和本地数据库可能直接丢失这会导致历史任务完全无法追溯。如果你的 DolphinScheduler 容器化部署了排查问题前一定要先确认日志是否持久化到了宿主机否则你想查日志都无从下手。这也是很多团队坚持用 Docker 但遇到问题只能重建服务、无法排查的原因。5.5 一些值得长期保留的排查习惯最后分享几个我个人养成的排查习惯谈不上多高大上但关键时刻非常管用。第一个习惯是定期检查t_ds_task_instance和t_ds_process_instance的表体积和索引情况。DolphinScheduler 跑久了这两张表会变得非常大尤其是没做定期清理的场景表体积上百 GB 都不奇怪。表太大会导致状态更新的 SQL 变慢进而引起状态回写延迟甚至失败。我的做法是给这两张表设置定期归档任务把已经结束的历史实例迁到历史库核心表现在始终保持一个可控的规模。第二个习惯是给数据库账号设置只读账号用于排查。用只读账号做初查避免手滑执行了不该执行的更新操作。只有确认需要做数据库修复时再用有写权限的账号操作。第三个习惯是维护一份“异常状态快速恢复文档”把常用的 SQL、状态枚举、排查思路记录下来。调度系统的问题通常比较紧急临时翻源码、翻文档会浪费很多时间提前准备好一套标准动作能让恢复时间从小时级缩短到分钟级。最后再说两句DolphinScheduler 作为开源调度系统功能确实很强但生产环境的稳定性需要运维人员对它的状态流转机制有足够理解。任务状态异常尤其僵尸任务问题归根结底是状态机的一致性问题。只要吃透了状态流转的链路了解哪些情况下状态会被写回、哪些情况下会卡住排查思路就会清晰很多。我个人在实际操作中最深的体会是处理这类问题永远把“确认底层进程是否真的没了”当作第一优先级其次才是改数据库。很多次返工都是因为跳过了这一步结果改完状态又被回写覆盖白白浪费了几个小时。另外数据库修复前一定要做好备份这是底线——虽然大多数时候用不上备份但一旦用上就是救命的。如果你也遇到过类似的问题希望这篇整理对你有帮助。以后如果再遇到 DolphinScheduler 任务状态异常记得先想清楚状态机再动手改库别一上来就重启服务。
返回列表