2009-12-21 110 views
2

考慮下面的Perl代碼:MySql:事務不檢測死鎖?

$schema->txn_begin(); 

my $r = $schema->resultset('test1')->find({id=>20}); 

my $n = $r->num; 
$r->num($n+1); 
print("updating for $$\n"); 
$r->update(); 

print("$$ val: ".$r->num."\n"); 

sleep(4); 

$schema->txn_commit(); 

我期待的是,由於更新由事務保護的,那麼,如果兩個進程嘗試更新「編號」欄,第二個應該失敗,一些錯誤的原因它失去了比賽。 Interbase稱這爲「死鎖」錯誤。然而,MySQL會在update()調用中暫停,但在第一個調用提交之後,它將繼續愉快地繼續。第二個過程然後具有num的「舊」值,導致增量不正確。觀察:

$ perl trans.pl & sleep 1 ; perl trans.pl 
[1] 5569 
updating for 5569 
5569 val: 1015 
updating for 5571 
5571 val: 1015 
[1]+ Done     perl trans.pl 

在這兩種情況下結果值都是「1015」。這怎麼可能是正確的?

+2

這是一個InnoDB表嗎? MySQL的其他存儲引擎不支持事務。 – friedo 2009-12-21 20:47:45

+0

是的,我忘了提及這是一個引用InnoDB表的perl DBIx類。 – 2009-12-23 20:19:44

回答

5

假設你使用InnoDB作爲你的存儲引擎,這是我期望的行爲。 InnoDB的默認事務隔離級別爲REPEATABLE READ。這意味着當您執行SELECT時,交易在該特定時間獲得數據庫的snapshot。快照將而不是包括來自尚未提交的其他事務的更新數據。由於每個進程中的SELECT都在提交之前發生,因此它們每個都會看到數據庫處於相同狀態(num = 1014)。

爲了讓你似乎在期待的行爲,你應該遵循聖路易斯的建議和執行SELECT ... FOR UPDATE來鎖定你感興趣的行。要做到這一點,改變這一行

my $r = $schema->resultset('test1')->find({id=>20}); 

到這

my $r = $schema->resultset('test1')->find({id=>20}, {for=>'update'}); 

並重新運行您的測試。

如果您不熟悉MySQL中事務的複雜性,我強烈建議您閱讀文檔中有關InnoDB Transaction Model and Locking的部分。另外,如果您尚未閱讀,請仔細閱讀有關交易的DBIC Usage Notes以及AutoCommit。當AutoCommit打開或關閉時,txn_方法的行爲方式有點棘手。如果你喜歡它,我還建議閱讀源代碼。就我個人而言,我必須閱讀源代碼才能完全理解DBIC正在做什麼,以便我能夠獲得我想要的確切行爲。

+0

完美!使用for =>'update',腳本在第二個事務中獲取值之前停止。我在DBIx文檔中找到了「for」屬性的規範:http://search.cpan.org/~frew/DBIx-Class-0.08115/lib/DBIx/Class/ResultSet.pm#for – 2009-12-23 20:24:39

+0

非常好!在我所鏈接的所有東西中,我無法相信我遺漏了「for」屬性。對於那個很抱歉。 – bish 2009-12-23 20:45:15

0

嘗試將$ r-> num存儲在mysql變量而不是perl中。 對不起,我不知道Perl的,但基本上你想要的是

START TRANSACTION; 
SELECT num INTO @a FROM test1 where id = 20; 
UPDATE test1 set num=(@a+1) WJERE id=20; 
COMMIT; 
+1

你也可以在你的第一個SELECT之前發出一個LOCK,這將確保第一個在下一個允許選擇之前到達鎖定提交。 – MindStalker 2009-12-21 20:41:50

0

這不是僵局,僵局是這樣的:

TX1

1-更新R1 =>在R1 R2上寫鎖定 2-更新R2 =>寫鎖定

的Tx 2個

1-更新R2 2-更新R1

如果tx1和tx2同時執行,則可能發生tx1等待R2上的鎖定空閒,並且tx2等待R1上的鎖定。

對於你的情況,你需要鎖定id = 20的行(使用select進行更新)。到達「遲到」的tx將等待一段時間(由數據庫引擎定義)跟隨。