前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Oracle/Mysql迁移到Postgresql事务回滚行为差异及改造方法

Oracle/Mysql迁移到Postgresql事务回滚行为差异及改造方法

作者头像
mingjie
发布2022-05-12 10:15:51
1.1K0
发布2022-05-12 10:15:51
举报
文章被收录于专栏:Postgresql源码分析

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

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

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

汇总

Oracle事务内报错后的行为

代码语言:javascript
复制
            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);
    }

...

建表语句

代码语言:javascript
复制
create table t1 (i int);

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

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

代码语言:javascript
复制
=======写入后查询=======
I 1
=======异常后查询=======
I 1
=======提交后查询=======
I 1

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

Postgresql事务内报错后的行为

代码语言:javascript
复制
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报错后,再执行任何语句都会抛异常

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

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

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

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

commit后

数据没有写入:

迁移到Postgresql后如何改造?

方案一:PL/pgSQL

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

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

代码语言:javascript
复制
[ <<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伪代码

代码语言:javascript
复制
try 
    select xxx from t1
    
    xxx列存在的处理逻辑
catch
    xxx列存不存在的处理逻辑

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

On Postgresql伪代码

代码语言:javascript
复制
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列存在的处理逻辑
}

PG功能非常丰富,各种场景应该都可以找到替代方案

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2020-05-09,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 汇总
  • Oracle事务内报错后的行为
  • Postgresql事务内报错后的行为
    • 第一个差异点:事务内SQL报错后,再执行任何语句都会抛异常
      • 第二个差异点:报错后,事务自动回滚,会话状态处于idle in transaction (aborted)
      • 迁移到Postgresql后如何改造?
        • 方案一:PL/pgSQL
          • 方案二:寻找替代逻辑,避免事务内产生错误
          相关产品与服务
          数据库
          云数据库为企业提供了完善的关系型数据库、非关系型数据库、分析型数据库和数据库生态工具。您可以通过产品选择和组合搭建,轻松实现高可靠、高可用性、高性能等数据库需求。云数据库服务也可大幅减少您的运维工作量,更专注于业务发展,让企业一站式享受数据上云及分布式架构的技术红利!
          领券
          问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档