插入许多行会导致与Hibernate和Postgres的锁定冲突,从而使表为空

时间:2019-06-28 11:12:22

标签: postgresql hibernate

我们正在对一些查询进行基准测试,以查看它们是否仍然可以对“大量”数据可靠地工作。 (说实话,一百万并不算多,但是Postgres已经在这里失败了,所以显然是。)

我们用于调用此查询的Java代码如下所示:

@PersistenceContext
private EntityManager em;
@Resource
private UserTransaction utx;

for (int i = 0; i < 20; i++) {
    this.utx.begin();
    for (int inserts = 0; inserts  < 50_000; inserts ++) {
        em.createNativeQuery(SQL_INSERT).executeUpdate();
    }
    this.utx.commit();


    for (int parameter = 0; parameter  < 25; parameter ++) 
        long time = System.currentTimeMillis();
        Assert.assertNotNull(this.em.createNativeQuery(SQL_SELECT).getResultList());
        System.out.println(i + " iterations \t" + parameter  + "\t" + (System.currentTimeMillis() - time) + "ms");
    }
}

或使用普通JDBC:

Connection connection = //...


for (int i = 0; i < 20; i++) {
    for (int inserts = 0; inserts  < 50_000; inserts ++) {
        try (Statement statement = connection.createStatement();) {
            statement.execute(SQL_INSERT);
        }
    }

    for (int parameter = 0; parameter  < 25; parameter ++) 
        long time = System.currentTimeMillis();

        try (Statement statement = connection.createStatement();) {
            statement.execute(SQL_SELECT);
        }
        System.out.println(i + " iterations \t" + parameter  + "\t" + (System.currentTimeMillis() - time) + "ms");
    }
}

我们尝试的查询是将简单的INSERT插入带有JSON的表中,并将INSERT插入两个约25行的表中。 SELECT有一个或两个JOIN,非常简单。一组查询是(我必须匿名化SQL,否则将不允许发布它):

CREATE TABLE ts1.p (
    id integer NOT NULL,
    CONSTRAINT p_pkey PRIMARY KEY ("id")
);

CREATE TABLE ts1.m(
    pId integer NOT NULL,
    mId character varying(100) NOT NULL,
    a1 character varying(50),
    a2 character varying(50),
    CONSTRAINT m_pkey PRIMARY KEY (pI, mId)
);

CREATE SEQUENCE ts1.seq_p;

/* 
 * SQL_INSERT 
 */

WITH p AS (
    INSERT INTO  ts1.p (id)
           VALUES (nextval('ts1.seq_p'))
    RETURNING id AS pId
)
INSERT INTO ts1.m(pId, mId, a1, a2)
      VALUES ((SELECT pId from p), 'M1', '11', '12'),
             ((SELECT pId from p), 'M2', '13', '14'),
             /*  ... about 20 to 25 rows of values */

/* 
 * SQL_SELECT 
 */

WITH userInput (mId, a1, a2) AS (
  VALUES
    ('M1', '11', '11'),
    ('M2', '12', '15'),
    /* ... about "parameter" rows of values */
)
SELECT m.pId, COUNT(m.a1) AS matches
FROM userInput u
    LEFT JOIN ts1.m m ON (m.mId) = (u.mId)
WHERE (m.a1 IS NOT DISTINCT FROM u.a1) AND
    (m.a2 IS NOT DISTINCT FROM u.a2) OR
    (m.a1 IS NULL AND m.a2 IS NULL)
GROUP BY m.pId

/* plus HAVING, additional WHERE clauses etc. according to the use case, but that just speeds up the query */

执行时,我们得到以下输出(这些值应该稳定且线性地增加):

271ms
414ms
602ms
820ms
995ms
1192ms
1396ms
1594ms
1808ms
1959ms
110ms
33ms
14ms
10ms
11ms
10ms
21ms
8ms
13ms
10ms

如您所见,在获得一些值(通常在大约300,000至500,000插入)之后,查询所需的时间大大减少。遗憾的是,我们无法真正调试当时的结果(除了它不是null之外),但是我们假定它是一个空列表,因为数据库表是空的。

让我重复一遍:INSERTS经过一百万次之后,Postgres清除了表。

当然这是完全不能接受的。

我们尝试了各种不同的查询,所有这些查询都从中级到中级,并且都产生了这种行为,因此我们假设这不是查询。

我们认为该序列可能返回的值对于列integer而言过高,因此我们删除并重新创建了该序列。

一旦出现此异常:

org.postgresql.util.PSQLException : FEHLER: Verklemmung (Deadlock) entdeckt
  Detail: Prozess 1620 wartet auf AccessExclusiveLock-Sperre auf Relation 2001098 der Datenbank 1937678; blockiert von Prozess 2480.

我完全无法翻译。我猜是这样的:

org.postgresql.util.PSQLException : ERROR: Jamming? Clamping? Constipation? (Deadlock) found

但是我认为此错误与清除表无关。我们只是针对错误的数据库进行了测试,因此在同一张表上运行了多个查询。通常每个基准测试只有一个数据库。

当然,重要的是我们要找出错误所在,以便我们可以决定是否对客户丢失数据有任何风险(因为再次出现错误,数据库清空了它选择的某些表)。

Postgres版本: PostgreSQL 10.6, compiled by Visual C++ build 1800, 64-bit

我们也尝试了PostgreSQL 9.6.11, compiled by Visual C++ build 1800, 64-bit。而且我们在那里从来没有遇到过同样的问题(即使运气不好,因为它不是100%可重复的)。

您知道错误是什么吗?或者我们如何调试它?整个基准测试运行一个小时,因此没有即时反馈。

0 个答案:

没有答案