Oracle/Mysql迁移到Postgresql事务回滚行为差异(开发避坑系列)

本文涉及的产品
RDS MySQL Serverless 基础系列,0.5-2RCU 50GB
RDS MySQL Serverless 高可用系列,价值2615元额度,1个月
云数据库 RDS MySQL,高可用系列 2核4GB
简介: Mysql或Oracle迁移到Postgresql系产品后,经常会发生事务回滚导致的问题,具体问题一般都是类似于: **为什么我没rollback,我的事务就自己回滚了?** 下面我举一个简单的例子,说明下PG和其他两款DB在事务回滚行为上的差异 ## Oracle事务内报错后的行为 (完整代码贴在文章最后) ```java Class.fo

Mysql或Oracle迁移到Postgresql系产品后,经常会发生事务回滚导致的问题,具体问题一般都是类似于:

为什么我没rollback,我的事务就自己回滚了?

下面我举一个简单的例子,说明下PG和其他两款DB在事务回滚行为上的差异

Oracle事务内报错后的行为

(完整代码贴在文章最后)

            Class.forName("oracle.jdbc.driver.OracleDriver");
            conn = DriverManager.getConnection(URL, USER, PASSWORD);
            conn.setAutoCommit(false);
            Statement stmt = conn.createStatement();

            /* 事务启动,写入一些数据 */
            stmt.executeUpdate("INSERT INTO t1 VALUES (1)");

            /* 查询t1全部数据 */
            selectAllFromTable(stmt, "写入后查询");

            /* 制造一些错误,这里查询不存在的列 */
            try {
                stmt.executeQuery("select xxxxxxx from t1 ");
            } catch (Exception e) {
                /* 那么到这里事务是否已经回滚了?*/
                selectAllFromTable(stmt, "异常后查询");
            }
            conn.commit();
            selectAllFromTable(stmt, "提交后查询");

...

private static void selectAllFromTable(Statement stmt, String info) throws SQLException {
        System.out.println("=======" + info + "=======");
        ResultSet rs1 = stmt.executeQuery("SELECT i FROM t1");
        ResultSetMetaData rsMetaData1 = rs1.getMetaData();
        printRs(rs1, rsMetaData1);
    }

...

建表语句

create table t1 (i int);

我们可以猜一下三次selectAllFromTable(函数就是简单的查全表)输出会是什么

用Mysql或Oracle的同学可能直接就可以想到:

=======写入后查询=======
I 1
=======异常后查询=======
I 1
=======提交后查询=======
I 1

这里关键就是 出现查询异常后,不影响事务的正常运行,后面可以继续在事务内操作。但在PG中就不一样了。

Postgresql事务内报错后的行为

public class TestPgsql {

    private static final String URL = "jdbc:postgresql://121.196.26.196:7001/postgres";
    private static final String USER = "postgres";
    private static final String PASSWORD = "333";

    public static void main(String[] args) {
        Connection conn = null;
        try {
            Class.forName("org.postgresql.Driver");
            conn = DriverManager.getConnection(URL, USER, PASSWORD);
            conn.setAutoCommit(false);
            Statement stmt = conn.createStatement();

            /* 事务启动,写入一些数据 */
            stmt.executeUpdate("INSERT INTO t1 VALUES (1)");

            /* 查询t1全部数据 */
            selectAllFromTable(stmt, "写入后查询");

            /* 制造一些错误,这里查询不存在的列 */
            try {
                stmt.executeQuery("select xxxxxxx from t1 ");
            } catch (Exception e) {
                /* 那么到这里事务是否已经回滚了?*/
                selectAllFromTable(stmt, "异常后查询");

            }
            conn.commit();
            selectAllFromTable(stmt, "提交后查询");

...

这里就不再贴报错了,我贴下单步调试的过程更容易理解

第一个差异点:事务内SQL报错后,再执行任何语句都会抛异常

image.png

在报错后的事务内再执行查询,报PG的标准错误:
org.postgresql.util.PSQLException: ERROR: current transaction is aborted, commands ignored until end of transaction block

image.png

第二个差异点:报错后,事务自动回滚,会话状态处于idle in transaction (aborted)

image.png

那么在SQL报错后,为了之前的修改能生效,我在报错后的异常处理时直接提交可以吗?

不可以,在报错时事务已经回滚,虽然提交没有报错,但是写入的数据不会生效

image.png

commit后
image.png

数据没有写入:
image.png

迁移到Postgresql后如何改造?

方案一:PL/pgSQL

使用Postgresql提供的PL/pgSQL语法,将相关逻辑写入PG的函数中,使用PG的EXCEPTION语法封装响应的处理逻辑,在业务代码中调用函数即可保证事务不会中断。

https://www.postgresql.org/docs/9.1/plpgsql-control-structures.html#PLPGSQL-ERROR-TRAPPING

[ <<label>> ]
[ DECLARE
    declarations ]
BEGIN
    statements
EXCEPTION
    WHEN condition [ OR condition ... ] THEN
        handler_statements
    [ WHEN condition [ OR condition ... ] THEN
          handler_statements
      ... ]
END;
If no error occurs, this for

方案二:寻找替代逻辑,避免事务内产生错误

例如这样的业务逻辑(来自云上客户):

On Oracle伪代码

try 
    select xxx from t1
    
    xxx列存在的处理逻辑
catch
    xxx列存不存在的处理逻辑

那么到Postgresql可以采用无异常的处理方式:

On Postgresql伪代码


int n = select count(*) from information_schema.columns WHERE table_schema = 'postgres' and table_name = 't1' and column_name = 'xxx';

if (n == 0) {
    xxx列存不存在的处理逻辑
} else {
    xxx列存在的处理逻辑
}
相关实践学习
每个IT人都想学的“Web应用上云经典架构”实战
本实验从Web应用上云这个最基本的、最普遍的需求出发,帮助IT从业者们通过“阿里云Web应用上云解决方案”,了解一个企业级Web应用上云的常见架构,了解如何构建一个高可用、可扩展的企业级应用架构。
MySQL数据库入门学习
本课程通过最流行的开源数据库MySQL带你了解数据库的世界。 &nbsp; 相关的阿里云产品:云数据库RDS MySQL 版 阿里云关系型数据库RDS(Relational Database Service)是一种稳定可靠、可弹性伸缩的在线数据库服务,提供容灾、备份、恢复、迁移等方面的全套解决方案,彻底解决数据库运维的烦恼。 了解产品详情:&nbsp;https://www.aliyun.com/product/rds/mysql&nbsp;
目录
相关文章
|
3月前
|
关系型数据库 MySQL 数据库
自建数据库如何迁移至RDS MySQL实例
数据库迁移是一项复杂且耗时的工程,需考虑数据安全、完整性及业务中断影响。使用阿里云数据传输服务DTS,可快速、平滑完成迁移任务,将应用停机时间降至分钟级。您还可通过全量备份自建数据库并恢复至RDS MySQL实例,实现间接迁移上云。
|
4月前
|
存储 运维 关系型数据库
从MySQL到云数据库,数据库迁移真的有必要吗?
本文探讨了企业在业务增长背景下,是否应从 MySQL 迁移至云数据库的决策问题。分析了 MySQL 的优势与瓶颈,对比了云数据库在存储计算分离、自动化运维、多负载支持等方面的优势,并提出判断迁移必要性的五个关键问题及实施路径,帮助企业理性决策并落地迁移方案。
|
6月前
|
人工智能 运维 关系型数据库
数据库运维:mysql 数据库迁移方法-mysqldump
本文介绍了MySQL数据库迁移的方法与技巧,重点探讨了数据量大小对迁移方式的影响。对于10GB以下的小型数据库,推荐使用mysqldump进行逻辑导出和source导入;10GB以上可考虑mydumper与myloader工具;100GB以上则建议物理迁移。文中还提供了统计数据库及表空间大小的SQL语句,并讲解了如何使用mysqldump导出存储过程、函数和数据结构。通过结合实际应用场景选择合适的工具与方法,可实现高效的数据迁移。
1054 1
|
3月前
|
关系型数据库 MySQL 数据库
阿里云数据库RDS费用价格:MySQL、SQL Server、PostgreSQL和MariaDB引擎收费标准
阿里云RDS数据库支持MySQL、SQL Server、PostgreSQL、MariaDB,多种引擎优惠上线!MySQL倚天版88元/年,SQL Server 2核4G仅299元/年,PostgreSQL 227元/年起。高可用、可弹性伸缩,安全稳定。详情见官网活动页。
|
3月前
|
关系型数据库 分布式数据库 数据库
阿里云数据库收费价格:MySQL、PostgreSQL、SQL Server和MariaDB引擎费用整理
阿里云数据库提供多种类型,包括关系型与NoSQL,主流如PolarDB、RDS MySQL/PostgreSQL、Redis等。价格低至21元/月起,支持按需付费与优惠套餐,适用于各类应用场景。
|
6月前
|
SQL 关系型数据库 MySQL
Go语言数据库编程:使用 `database/sql` 与 MySQL/PostgreSQL
Go语言通过`database/sql`标准库提供统一数据库操作接口,支持MySQL、PostgreSQL等多种数据库。本文介绍了驱动安装、连接数据库、基本增删改查操作、预处理语句、事务处理及错误管理等内容,涵盖实际开发中常用的技巧与注意事项,适合快速掌握Go语言数据库编程基础。
480 62
|
3月前
|
关系型数据库 MySQL 数据库
阿里云数据库RDS支持MySQL、SQL Server、PostgreSQL和MariaDB引擎
阿里云数据库RDS支持MySQL、SQL Server、PostgreSQL和MariaDB引擎,提供高性价比、稳定安全的云数据库服务,适用于多种行业与业务场景。
|
6月前
|
关系型数据库 MySQL 数据库
MySQL数据库上云迁移
本文介绍了将数据库迁移到RDS for Mysql的两种主要方法:停服迁移和不停服迁移。停服迁移适合可短暂中断服务的场景,通过mysqldump或DTS完成;不停服迁移适用于需保持业务连续性的场景,推荐使用DTS实现结构、全量及增量数据迁移。文中详细列出了每种方法的具体操作步骤,帮助企业根据需求选择合适的迁移方案。
190 1
MySQL数据库上云迁移
|
7月前
|
Oracle 关系型数据库 数据库
【赵渝强老师】在PostgreSQL中访问Oracle
本文介绍了如何在PostgreSQL中使用oracle_fdw扩展访问Oracle数据库数据。首先需从Oracle官网下载三个Instance Client安装包并解压,设置Oracle环境变量。接着从GitHub下载oracle_fdw扩展,配置pg_config环境变量后编译安装。之后启动PostgreSQL服务器,在数据库中创建oracle_fdw扩展及外部数据库服务,建立用户映射。最后通过创建外部表实现对Oracle数据的访问。文末附有具体操作步骤与示例代码。
241 6
【赵渝强老师】在PostgreSQL中访问Oracle
|
8月前
|
Oracle 关系型数据库 MySQL
Oracle linux 8 二进制安装 MySQL 8.4企业版
Oracle linux 8 二进制安装 MySQL 8.4企业版
295 1

热门文章

最新文章

推荐镜像

更多