2012-02-04 140 views
10

我有一個PHP腳本運行一個SELECT查詢,然後立即刪除記錄。有多臺機器正在ping同一個php文件並從同一張表中獲取數據。每臺遠程計算機都在cron作業上運行。SELECT然後立即刪除mysql記錄

我的問題是,有時它不能夠快速刪除,因爲一些機器在同一時間ping。

我的問題是,我如何從數據庫中選擇一條記錄,並在下一臺機器抓取它之前將其刪除。現在我只是加了一個短暫的延遲,但效果不好。我嘗試使用交易,但我認爲它不適用於此。

這裏是我的腳本的示例代碼片段:

<?php 

$query = "SELECT * FROM `queue` LIMIT 1"; 
$result = mysql_query($query) or die(mysql_error()); 

while($row = mysql_fetch_array($result)){ 
    $email = $row['email']; 
    $campaign_id = $row['campaign']; 
} 

$queryx = "DELETE FROM `queue` WHERE `email` = '".$email."'"; 
$resultx = mysql_query($queryx) or die(mysql_error()); 

?> 

真正體會到了幫助。

+2

它怎麼不適用?聽起來*完全*就像交易是有益的。 – mpen 2012-02-04 01:53:00

+0

您允許使用存儲過程嗎? – dvicino 2012-02-04 01:53:03

+0

@Mark - 交易是否也會阻止SELECT操作?我想知道這可能不是問題。@john - 如果這些由'cron'運行,'die(mysql_error())'有什麼意義?爲什麼不把錯誤記錄到文件或其他東西? – 2012-02-04 01:55:06

回答

-3

把你的刪除查詢放在while循環中,只是想增加你的選擇限制。

<?php 
$query = mysql_query("SELECT * FROM `queue` LIMIT 1") or die(mysql_error()); 

while($row = mysql_fetch_array($query)){ 
    mysql_query("DELETE FROM `queue` WHERE `email` = '" . $row['email'] . "' LIMIT 1") or die(mysql_error()); 
} 
?> 

上面的代碼是一樣的跑步:

mysql_query("DELETE FROM `queue` LIMIT 1") or die(mysql_error()); 

小心使用刪除查詢,如果電子郵件字段爲空,它會刪除具有空白電子郵件中的所有行。將LIMIT 1添加到您的刪除查詢中,以避免多行被刪除。

要添加一個隨機的延遲,你可以一個sleep添加到腳本的頂部,

如:

<?php 
$seconds = mt_rand(1,10); 
sleep($seconds); 
?> 
+1

我不知道這將消除競爭條件/爭用問題,它只是「縮短」「可能」活動的數量。它可以在某些情況下工作,但仍然可能存在異常情況。請注意,由於有'LIMIT 1','while()'是多餘的。所以我不知道這真的會在最後做任何有效的工作...... – 2012-02-04 02:12:20

+0

如果他決定將SELECT更改爲多於1個結果,則刪除查詢將僅在第一個結果上運行。所以最終它更有效率,並且沒有不同的'資源明智的'使DELETE在循環之外。 – 2012-02-04 02:12:52

+0

「如果」?有很多if。如果OP彈出一個隊列,我想它只是一個。這一點仍然是相同的(並且如果對於稍後處理的結果返回許多結果,則放大)。 – 2012-02-04 02:14:31

6

好,我會用表鎖 read more here

鎖是安全的並適用於一個客戶端會話。 表鎖只能保護其他會話不適當的讀取或寫入。

1

運行更新查詢,將在您選擇之前更改密鑰。通過這個新鍵進行選擇,只有在同一個會話中才能知道。
如果表是innoDB,則記錄被鎖定,並且當它被釋放時,其他選擇將不會找到記錄。

+1

[SELECT ... FOR UPDATE和SELECT ...鎖定在分享模式鎖定讀取](http://dev.mysql.com/doc/refman/5.0/en/innodb-locking-reads.html)。 – 2012-02-04 02:06:37

+0

唯一的問題,我有我的意見上面,你可以'刪除'從'SELECT ... FOR UPDATE'模式?或者你首先必須解鎖(並因此回到我們開始的位置)? – 2012-02-04 02:08:28

3

您應該按如下方式使用子查詢......

<?php 

$queryx = "DELETE FROM `queue` WHERE `email` IN (SELECT email FROM `queue` LIMIT 1)"; 
$resultx = mysql_query($queryx) or die(mysql_error()); 

?> 

*注意:永遠只選擇你想要的字段...儘量避免選擇* ...這會減慢性能

+0

有趣。如果重點是通過「隨機」電子郵件清除所有排隊的行,這可以工作。 – 2012-02-04 02:28:30

+0

另外,'SELECT *'是很好的建議。 'SELECT email'應該足夠了。 – 2012-02-04 02:33:55

+0

你不能在子查詢中使用LIMIT。否則,這正是我想要做的。 – john 2012-02-04 04:12:52

4

如果您正在使用MariaDB的10:

DELETE FROM `queue` LIMIT 1 RETURNING * 

Documentation

+0

這很酷,但僅適用於MariaDB 10.0.5或更新的版本 – 2017-09-05 17:01:00