2015-08-27 41 views
1

我使用libpqxx來連接到postgresql。一切都很好,直到我在一行上的一個表上運行可查詢的查詢。postgresql永久掛在可序列化事務

表:

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函數中。爲什麼?我認爲它必須回滾一個交易嗎?但不是?只是掛起。

更新:純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); 

的PostgreSQL 9.1 - 相同的懸掛

+0

使用不同的隔離級別時會發生什麼情況。難道你沒有得到相同的結果嗎? – sstan

+0

可能是dba.SE的一個很好的候選人。 –

+0

它不是真的掛在下一行嗎? 't2.commit(); //掛在這裏' –

回答

0

如果僅在兩個交易都參與其中,你應該得到的交易T2獨特的違規錯誤 - 完全一樣與默認READ COMMITTED隔離級別:

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

T1第一次嘗試插入並贏得不管哪個事務嘗試提交第一。所有下面的事務試圖插入相同的密鑰等待第一個。再次,這對於READ COMMITTEDSERIALIZABLE都是有效的。

一個可能的解釋將是一個第三交易參與,它試圖插入相同的密鑰第一,仍然是開放的。或者幾個這樣的交易,你的測試的文物。

所有交易等待第一個試圖插入相同的密鑰。如果那個提交,所有其他提交一個獨特的違規。如果它回滾,下一個就會有機會。

要在pg_stat_activity檢查外觀(連接到相同數據庫):

SELECT * FROM pg_stat_activity; 

更具體地:

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

然後提交/回滾空閒事務。或蠻力:終止該連接。詳細信息:

+0

pg_stat_activity - 顯示這個2 thransaction(backend_start,xact_start,query_start,state_change,waiting,backend_xid,backend_xmin):t1(「2015-08-29 10:48:28.636 + 03」 ,「2015-08-29 10:48:28.683 + 03」,「2015-08-29 10:48:28.683 + 03」,「2015-08-29 10:48:28.683 + 03」,f,「idle在交易中「,757,_),t2(」2015-08-29 10:48:28.667 + 03「,」2015-08-29 10:48:28.683 + 03「,」2015-08-29 10:48 :28.683 + 03「,」2015-08-29 10:48:28.683 + 03「,t,」active「,758,757) – immortaldragon

1

掛起無關與serializable隔離級別。

我不是libpqxx專家,但您的示例似乎在一個線程中運行兩個事務。那是你的問題。

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

上面的語句必須等待t1在完成前提交或回滾,但t1.commit()從來沒有得到執行的機會。僵局!這是絕對正常的,無論您選擇什麼隔離級別,都會發生。這只是嘗試在同一個執行線程中從兩個併發事務運行語句的結果,而不是一個好主意。

嘗試在不同線程上運行這兩個事務,並且您的掛起將消失。

+0

單線程可能會解釋兩個事務對於死鎖是否足夠。 –

+0

一個線程 - 但不同的連接。不同的連接就像不同的程序。 – immortaldragon

+0

不是。單個線程中的不同連接與其他程序不同。要真正模擬一個正常的多用戶場景,每個連接需要1個線程。這樣做可以確保't1.commit();'最終執行,這會解除對't2.exec(「INSERT INTO t1(id)VALUES(25)」);'的阻止。但是,在一個線程中運行這兩個事務意味着't1.commit();'永遠不會有機會運行,這是解除阻止't2'所需的。結果:應用程序導致死鎖。數據庫在這裏沒有錯誤,並且完全按照預期工作。 – sstan

0

形式的Postgres團隊:

「在並行編程,死鎖是一種情況,即兩個或兩個以上相互競爭的行動都在等待對方來完成,因而永遠都不做。」 https://en.wikipedia.org/wiki/Deadlock

定義它首先不是死鎖。 「c1」不等待「c2」完成,並可以愉快地開始其id = 104的業務。然而,你的應用程序永遠不會給c1下一個命令,因爲它頑固地等待「c2」執行它的插入 - 它不能由於「c1」所持有的鎖定。

鎖定測試可以在一個線程內完成,但死鎖測試意味着兩個連接都需要同時嘗試執行一個命令 - 單個程序無需線程就無法完成。

David J.

+1

因此,它不是Postgres中的死鎖(它將通過終止一個(或多個)競爭性事務自動解決),但它仍然是您應用程序中的一個死鎖。 –