Postgresql永远挂在序列化事务上



我使用libpqxx连接到postgresql。一切都很好,直到我在一个表的一行上运行serialazable查询。

表:

CREATE TABLE t1(id integer primary key);

postgres 9.4.4_x64

pqxx::connection c1(conn_str);
pqxx::connection c2(conn_str);
pqxx::transaction<pqxx::isolation_level::serializable> t1(c1);
t1.exec("INSERT INTO t1 (id) VALUES (25)");
pqxx::transaction<pqxx::isolation_level::serializable> t2(c2);
t2.exec("INSERT INTO t1 (id) VALUES (25)"); //hang here
t2.commit();
t1.commit();

我的程序永远挂起。挂起PQexec函数。为什么?我认为它必须回滚一个事务吗?但没有?只是挂。

UPDATE: libpq:

c1 = PQconnectdb(conninfo);
c2 = PQconnectdb(conninfo);
res1 = PQexec(c1, "BEGIN");
PQclear(res1);

res1 = PQexec(c1, "INSERT INTO t1 (id) VALUES (104)");
PQclear(res1);
res2 = PQexec(c2, "BEGIN");
PQclear(res2);
res2 = PQexec(c2, "INSERT INTO t1 (id) VALUES (104)");
PQclear(res2);
res2 = PQexec(c2, "END");
PQclear(res2);
res1 = PQexec(c1, "END");
PQclear(res1);

挂起与serializable隔离级别无关。

我不是libpqxx专家,但是您的示例似乎在单个线程中运行两个事务。那是你的问题。

t2.exec("INSERT INTO t1 (id) VALUES (25)");

上面的语句必须等待t1提交或回滚才能完成,但t1.commit()永远没有机会执行。僵局!这是绝对正常的,无论您选择的隔离级别如何,都会发生这种情况。这只是试图在同一个执行线程中运行来自两个并发事务的语句的结果,这不是一个好主意。

尝试在不同的线程上运行两个事务,挂起将会消失。

如果只涉及两个事务,您应该得到一个唯一的冲突错误,事务t2 - 与默认READ COMMITTED隔离级别完全相同:

ERROR:  duplicate key value violates unique constraint "t1_pkey"
DETAIL:  Key (id)=(25) already exists.

t1先尝试插入,无论哪个事务先尝试commit,都将获胜。所有后续尝试插入相同密钥的事务都要等待第一个。同样,这对READ COMMITTEDSERIALIZABLE同样有效。

可能的解释是涉及第三个事务,该事务首先尝试插入相同的密钥,并且仍然打开。或者几个这样的事务,测试的工件。

所有事务等待第一个尝试插入相同键的事务。如果那个犯了,其他所有的都有唯一的违例。如果它回滚,下一行就有机会。

查看pg_stat_activity(连接到相同的数据库):
SELECT * FROM pg_stat_activity;

更具体地说:

SELECT * FROM pg_stat_activity WHERE state = 'idle in transaction';

然后提交/回滚空闲事务。或者使用暴力:终止该连接。细节:

    psql: FATAL: too many connections for role

form postgres team:

"在并发编程中,死锁是指两个或多个相互竞争的操作都在等待另一个操作完成,因此两个操作都没有完成。"https://en.wikipedia.org/wiki/Deadlock

定义它首先不是死锁。"c1"不等待"c2"完成,可以愉快地处理id=104的业务。但是,您的应用程序永远不会给c1下一个命令,因为它固执地等待"c2"执行插入操作——由于"c1"持有锁,它无法执行插入操作。

锁测试可以在单个线程中完成,但死锁测试意味着两个连接需要同时尝试执行一个命令——如果没有线程,单个程序无法完成。

David j .

相关内容

  • 没有找到相关文章

最新更新