DolphinScheduler 实战:解决 MySQL 事务处理中的异常问题及代码热替换

关于DolphinScheduler

Dolphin Scheduler是Apache组织开源的一款分布式易扩展的可视化工作流任务调度系统。适用于企业级场景,提供了一个可视化操作任务、工作流和全生命周期数据处理过程的解决方案。可以及时监控任务的执行状态,支持重试、指定节点恢复失败、暂停、恢复、终止任务等操作。

使用场景

我们利用它的工作流任务调度功能,定时执行SQL,可以对数据进行离线批处理操作。

9c451f07549d7af74890c71b49c57a3f.png

在处理MySQL数据时,存在一种场景,需要先删除表数据,再将新加工的数据插入表中,这样存在一个问题,在数据插入之前,业务系统会查询到空数据,所以我们往往会使用事务控制,将删除和插入操作放在同一个事务中,比如这样:

BEGIN;
DELETE FROM table_a ;
INSERT INTO table_a SELECT * from table_b left join table_c ...;
COMMIT;

问题描述

在执行到INSERT INTO这个过程的时候,查询出的数据重复,违反了主键约束

a07ac6bada5c03f496b680e62af8445a.png

这个时候任务执行失败,不再继续往下执行,所以COMMIT未执行。但是查询table_a的时候发现表被清空了!

问题分析

在MySQL中,如果没有使用连接池,并且在 SQL 任务异常后没有执行 commit 或 rollback,然后关闭了连接,数据库的行为取决于以下几个因素:

事务的行为
  1. 1. 自动提交(Auto-Commit)模式:

  • • 自动提交开启(默认):如果数据库连接的自动提交功能开启(通常 JDBC 默认是 auto-commit = true),每个 SQL 语句都会被视为一个独立的事务并在执行后立即提交。因此,即使异常发生,如果已经执行的 SQL 语句没有显式的回滚操作,它们可能已经被提交,无法撤销。

  • • 自动提交关闭:如果你手动关闭了自动提交(即 auto-commit = false),所有 SQL 语句会在一个事务中执行,只有显式调用 commit() 才会提交。如果在 SQL 任务过程中发生异常,并且没有手动执行 rollback(),再关闭连接时,数据库通常会自动回滚该事务。

2. 关闭连接后的行为:

  • • 无连接池的情况下,如果关闭连接且事务尚未提交或回滚,大多数数据库(包括 MySQL 和 Oracle)会自动回滚当前未提交的事务。这是因为关闭连接通常意味着客户端已经放弃了这个事务,数据库出于一致性考虑,会自动清理未完成的事务,防止脏数据写入数据库。

查询DolphinScheduler源码

找到执行SQL任务的代码,可以看出,任务执行结束后,会将连接关闭。

f0e402dafbc93f68a3b3a25a8280209d.png

60a1e55e3d8596d1f4028585abd4972c.png

看一下close()方法的注释:

65a7bd18203994e95fbd03120005ba27.png

这段注释解释了 JDBC Connection 类的 close() 方法的行为和最佳实践。以下是对每个部分的详细解释:

释放资源
/**
 * Releases this <code>Connection</code> object's database and JDBC resources
 * immediately instead of waiting for them to be automatically released.
 */
  • • 释义:调用 close() 方法会立即释放与 Connection 对象相关联的数据库和 JDBC 资源。这意味着数据库连接和相关资源(例如语句和结果集)会被释放,而不是等待垃圾回收器或其他机制自动处理。

重复调用
/**
 * Calling the method <code>close</code> on a <code>Connection</code>
 * object that is already closed is a no-op.
 */
  • • 释义:如果你对一个已经关闭的 Connection 对象调用 close() 方法,该方法不会产生任何实际效果,也不会抛出异常。这是一种“无操作”(no-op)行为,即它不会改变连接的状态或引发任何错误。

显式提交或回滚的推荐
/**
 * It is <b>strongly recommended</b> that an application explicitly
 * commits or rolls back an active transaction prior to calling the
 * <code>close</code> method.  If the <code>close</code> method is called
 * and there is an active transaction, the results are implementation-defined.
 */
  • • 释义:强烈建议在调用 close() 方法之前,显式地提交 commit() 或回滚 rollback()活动的事务。这是因为:

    • • 如果在活动事务仍然存在的情况下调用 close(),具体行为取决于 JDBC 驱动的实现。

    • • 某些 JDBC 驱动可能会自动回滚未提交的事务,但这并不是标准行为。因此,确保在调用 close() 之前处理事务状态是最佳实践,以避免潜在的数据不一致或未定义的行为。

现在知道了,在执行到INSERT INTO的时候出现异常,连接关闭,我们没有执行COMMIT,也没有执行ROLLBACK,具体行为取决于 JDBC 驱动的实现,通过在连接关闭前打印日志,发现auto-commit = true

d238b80583aef2cedaf83e1328f83d13.png

6df0b2901d33d833c3bc3f757effb5fe.png

所以现在要做的就是,在任务执行异常的时候,执行 rollback()  ,看一下 rollback()  方法的注释有这样一句话

This method should be used only when auto-commit mode has been disabled.

rollback() 方法应该只在关闭自动提交模式 (auto-commit) 时使用。

355aca34f0c3c2539cc4a46a03f6c571.png

保险起见,在调用rollback()之前将auto-commit设置为false。

854df3534082c08a424b2fb15abd085b.png

替换线上代码

使用Arthas不停机替换线上代码,具体方法可以看之前的文章: Arthas不停机替换线上代码

94b17f4a3362cfc021f9f675894dc043.png

线上验证

事务正常回滚,delete操作被回滚,表数据没有被删除

f7af5f1478990528c4e2dbfc7ab29343.png

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值