由于事务之间的读/写依赖关系,无法序列化访问

2024-02-06

我最终成功地重现了序列化问题这个问题 https://stackoverflow.com/q/21706858/274677到 SSCCE(最短的独立完整示例)。我正在使用jdbc and java标签,尽管我相信这不是 Java 或 JDBC 特定的。

所以,我有两个表“主表”和“详细表”。 DETAIL TABLE 中的行挂在 MASTERTABLE 中的行下方:

CREATE TABLE public.MASTERTABLE(typename VARCHAR, i INTEGER);
ALTER TABLE public.MASTERTABLE ADD PRIMARY KEY (typename, i);

CREATE TABLE public.DETAILTABLE(typename VARCHAR, i INTEGER, j INTEGER);
ALTER TABLE public.DETAILTABLE ADD PRIMARY KEY (typename, i, j);
ALTER TABLE public.DETAILTABLE ADD CONSTRAINT detail_2_master FOREIGN KEY (typename, i) REFERENCES public.MASTERTABLE(typename, i);

然后我有代码(附加在最后),它在无限循环中执行以下操作:

  1. 在主表中插入给定的行typename
  2. 在步骤 #1 中添加的主表行下的详细表中插入 100 行。

当我在两个单独的实例中启动代码时,同时运行,并使用不同的typename参数(假设一个正在使用type-a和另一个type-b)几分钟后我得到“could not serialize access due to read/write dependencies among transactions“消息(跟踪在末尾)。

我不明白的是为什么 PostgreSQL 对两个并发事务感到困惑,因为它们显然正在访问表的不同“切片”:

  • 一个实例是使用 a 读取和写入行typename的价值 ”type-a"
  • 另一个实例是使用 a 读取和写入行typename value "type-b"

此外,PostgreSQL 知道typename是主表和明细表主键的一部分,因此保证访问的行集是不相交的。

我在代码下方附加了导致错误的典型调用以及引发的跟踪。

code

如果将下面的代码修改为使用事务隔离级别Connection.TRANSACTION_REPEATABLE_READ代替Connection.TRANSACTION_SERIALIZABLE错误条件不会被触发。我没有包含在重新运行之前清空表的代码,因为我试图使其尽可能短。

import javax.sql.*;
import java.sql.*;
import org.apache.commons.dbcp.BasicDataSource;
import org.apache.commons.dbutils.DbUtils;

public class FooMain {

    public static void main(String args[]) throws Exception {
        String typeName       =                 args[0];
        Integer transIsoLevel =    Connection.TRANSACTION_SERIALIZABLE; 

        DataSource ds = getDataSource("jdbc:postgresql://localhost:5432/your-database", "your-user-name", "your-user-password");
        Testing testingAPI = new Testing(ds, transIsoLevel);
        for (int i = 0 ; i < 1000*1000 ; i++) {
            System.out.printf("%s - %d\n", typeName, i);
            testingAPI.addMasterRow(typeName, i);
            for (int j = 0 ; j < 100 ; j++)
                testingAPI.addDetailRow(typeName, i);
        }
    }

    public static DataSource getDataSource(String dbURL, String user, String pwd) {
        BasicDataSource dS = new BasicDataSource();
        dS.setDriverClassName("org.postgresql.Driver");
        dS.setUsername(user);
        dS.setPassword(pwd);
        dS.setUrl(dbURL);
        dS.setMaxActive(1);
        dS.setMaxIdle(1);
        dS.setInitialSize(1); 
        dS.setValidationQuery("SELECT 1");
        return dS;
    }
}


class Testing {

    private DataSource ds;
    private int transactionLevel;

    public Testing(DataSource ds, int transactionLevel) {
        this.ds           = ds;
        this.transactionLevel = transactionLevel;
    }


    private Connection getConnection() throws SQLException {
        Connection conn = ds.getConnection();
        conn.setAutoCommit(false);
        conn.setTransactionIsolation(transactionLevel);
        return conn;
    }

    public void addMasterRow(String typeName, int i) throws SQLException {
        Connection conn = getConnection();
        PreparedStatement ps1 = null;
        ResultSet         rs1 = null;
        PreparedStatement ps2= null;

        try {
            conn.commit();
            {   // this select statement does nothing, but I believe it is needed
                // to trigger the error
                String SQL="SELECT COUNT(*) FROM public.mastertable  "+
                           "WHERE typename=?                         ";
                ps1 = conn.prepareStatement(SQL);
                ps1.setString   (1, typeName);
                rs1 = ps1.executeQuery();
            }
            {
                String SQL="INSERT INTO public.mastertable(typename, i)               "+
                           "VALUES(?, ?)                                              ";
                ps2 = conn.prepareStatement(SQL);
                ps2.setString   ( 1, typeName);
                ps2.setInt      ( 2, i);
                ps2.executeUpdate();
            }
            conn.commit();
        } finally {
            DbUtils.closeQuietly(             conn, ps1,              rs1);
            DbUtils.closeQuietly((Connection) null, ps2, (ResultSet) null);
        }
    }

    public void addDetailRow(String typeName, int i) throws SQLException {
        Connection conn = getConnection();
        PreparedStatement ps = null;
        try {
            conn.commit();
            String SQL="INSERT INTO public.detailtable(typename, i, j)              "+
                       "(SELECT ?, ?, COALESCE(MAX(j)+1,0) FROM public.detailtable  "+
                       "WHERE typename=? AND i=?)                                   ";
            ps = conn.prepareStatement(SQL);
            ps.setString   ( 1, typeName);
            ps.setInt      ( 2, i);
            ps.setString   ( 3, typeName);
            ps.setInt      ( 4, i);
            ps.executeUpdate();
            conn.commit();
        } finally {
            DbUtils.closeQuietly(conn, ps, (ResultSet) null);
        }
    }
}

调用

只需同时调用两个实例,一个具有“type-a”命令行参数,另一个具有“type-b”命令行参数:

java -classpath ... FooMain type-a &
java -classpath ... FooMain type-b &

trace

Exception in thread "main" org.postgresql.util.PSQLException: ERROR: could not serialize access due to read/write dependencies among transactions
Detail: Reason code: Canceled on conflict out to pivot 4720754, during read.
Hint: The transaction might succeed if retried.
Where: SQL statement "SELECT 1 FROM ONLY "public"."mastertable" x WHERE "typename"::pg_catalog.text OPERATOR(pg_catalog.=) $1::pg_catalog.text AND "i" OPERATOR(pg_catalog.=) $2 FOR SHARE OF x"
at org.postgresql.core.v3.QueryExecutorImpl.receiveErrorResponse(QueryExecutorImpl.java:2102)
at org.postgresql.core.v3.QueryExecutorImpl.processResults(QueryExecutorImpl.java:1835)
at org.postgresql.core.v3.QueryExecutorImpl.execute(QueryExecutorImpl.java:257)
at org.postgresql.jdbc2.AbstractJdbc2Statement.execute(AbstractJdbc2Statement.java:500)
at org.postgresql.jdbc2.AbstractJdbc2Statement.executeWithFlags(AbstractJdbc2Statement.java:388)
at org.postgresql.jdbc2.AbstractJdbc2Statement.executeUpdate(AbstractJdbc2Statement.java:334)
at org.apache.commons.dbcp.DelegatingPreparedStatement.executeUpdate(DelegatingPreparedStatement.java:105)
at org.apache.commons.dbcp.DelegatingPreparedStatement.executeUpdate(DelegatingPreparedStatement.java:105)
at Testing.addDetailRow(FooMain.java:98)
at FooMain.main(FooMain.java:18)

None

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

由于事务之间的读/写依赖关系,无法序列化访问 的相关文章

随机推荐

  • 将 C Struct 编组为 C# 委托的返回值

    我试图从绑定到本机函数的委托中按值返回一个小 8 字节 结构 但在针对 NET Framework 2 0 时遇到以下错误 在针对 4 0 时 代码似乎可以正常工作 testclient exe 中发生类型为 System AccessVi
  • 传递格式化的 NSDate

    我传递给实例方法的格式化日期未保存 打印正确的日期 有人可以帮忙吗 调用方法代码片段 NSDateFormatter dateFormatter NSDateFormatter alloc init autorelease dateForm
  • 卡在 PHP 查询上

    我创建了一个页面 允许用户更改密码和电子邮件 所有这些都有效 但由于某种原因 当我只想更改我的电子邮件时 我也得到了该字段当前密码不正确 电子邮件本身在数据库中发生了变化 但这显示了出来 我显然已经验证了它的显示 但我不确定如何编写一个新查
  • 如何让它更短(Pythonic)?

    我必须检查很多世界是否在字符串中 代码如下所示 if string 1 in var string or string 2 in var string or string 3 in var string or string n in var
  • 具有本机扩展的 Ruby gem 无法在 AWS Lambda 上运行

    我有一个 ruby 脚本 正在尝试在 AWS Lambda 上运行 我如何让它使用带有本机扩展的 Ruby gem 我已经通过安装了我的 Ruby gemsbundle install deployment并将它们包含在我的部署中 当我在
  • C# 更改系统区域设置

    需要将系统区域设置更改为不同的国家 地区 我尝试过 SystemParametersInfo GetKeyboardLayout 但没有帮助 如何更改控制台应用程序的 C 系统区域设置 e g Thread CurrentThread Cu
  • 更改大表中的mysql字段名称

    我有一个包含 2100 万行的表 我必须更改其中一个行名称 当我尝试使用查询 alter table company change id new id int 11 时 查询永远不会结束 有没有简单的方法来更改大mysql表字段名称 首先
  • Excel:查找数组中的最后一个值

    我有一个数据集 gt A b c d AA BB gt 1 2 3 4 gt apple apple apple gt orange pear pear apple pear gt grapefruit grape grape grape
  • Eclipse 在 Ubuntu 14.04 中无法启动

    我尝试在 Ubuntu 14 04 中启动 Eclipse 时遇到问题 启动图片弹出 然后闪烁 变成白色 直到王国来临之前什么也没有发生 我尝试过 Luna Kepler 和 3 8 来自 Ubuntu 存储库 EE 和 SE 版本 两者相
  • Service Worker 可以缓存 POST 请求吗?

    我尝试在 fetch 事件的服务工作人员中缓存 POST 请求 I used cache put event request response 但返回的承诺被拒绝TypeError Invalid request method POST 当
  • 如何在打印语句中使用零填充标志精确打印两个位置

    如果想使用零垫进行此方法打印 你该怎么做 int month day public void printNumeric System out printf month day n i would like the month if it i
  • Tkinter ttk 查看自定义主题设置

    使用后ttk Style theme create name settings 可以看到该主题的设置吗 我问的原因是当我创建一个新主题并添加ttk Notebook root 对于我的代码 选项卡有圆角 这是我不想要的 这是一个例子 imp
  • 通过引用传递给构造函数

    我决定看看为成员分配引用是否会使成员成为引用 我编写了以下代码片段来测试它 有一个简单的类Wrapper与std string作为成员变量 我采取采取const string 在构造函数中并将其分配给公共成员变量 后来在main 方法我修改
  • Sublime Text 3 Windows 使用 Alt 选择列?

    Shift right click feels unintuitive to me How can I tell ST3 to allow Alt drag to do column selection like in many other
  • 为什么要重写 DTO 中的 toString 方法

    我看到大多数时候在DTO对象中 toString 方法实际上被重写了 例如 public class Person implements Serializable private String firstName private Strin
  • 使用 wc_price 过滤器挂钩向产品价格添加其他货币

    根据我原来帖子的答案使用时遇到格式不正确的数值wc priceWooCommerce 挂钩 https stackoverflow com questions 66084833 a non well formed numeric value
  • 学C要多长时间? [关闭]

    就目前情况而言 这个问题不太适合我们的问答形式 我们希望答案得到事实 参考资料或专业知识的支持 但这个问题可能会引发辩论 争论 民意调查或扩展讨论 如果您觉得这个问题可以改进并可能重新开放 访问帮助中心 help reopen questi
  • Fallocate 和 ftruncate 之间有什么区别

    根据我的测试 他们都可以改变文件大小 为什么他们都可以将文件变大或变短 Fallocate 和 ftruncate 和有什么区别 ftruncate是一个简单的 单一用途的函数 根据 POSIX 文档 http pubs opengroup
  • 汇编程序中的寻址

    有件事我无法消化 我正在学习一些汇编程序 现在我正在学习寻址章节 我理解用于解除引用的括号的概念 但不知怎的 当我看到它的用法时 我就是无法理解它的要点 更准确地说 我的困惑是从这里开始的 mov al L1 在这里 我假设 L1 作为示例
  • 由于事务之间的读/写依赖关系,无法序列化访问

    我最终成功地重现了序列化问题这个问题 https stackoverflow com q 21706858 274677到 SSCCE 最短的独立完整示例 我正在使用jdbc and java标签 尽管我相信这不是 Java 或 JDBC