2012-05-28 73 views
0

我有一個執行mysql pdo查詢的php腳本。在這個腳本中有幾個讀取和寫入相同的表當php腳本執行順序相同的腳本快速啓動兩次時mysql腳本查詢

舉例來說,假設有4個查詢,一個讀取,一個寫入,另一個讀取,另一個寫入,每個讀取需要10秒執行,每個寫入需要0.1秒執行。

如果我在1/100秒內從cli nohup php execute_queries.php &執行這個腳本兩次,查詢的執行順序是什麼?

在來自第二個實例的查詢開始運行之前,是否需要完成腳本第一個實例的所有查詢,或者在表被鎖定寫入之前第一次從這兩個實例讀取開始和結束?

注意:假設我使用MyISAM和發送寫入更新的記錄(IE,整個表被寫入時鎖定。)

回答

1

既然你不使用事務,那麼沒有的不會等待一個腳本中的所有查詢完成,因此查詢可能會重疊。

有一個叫做併發編程的完整研究領域,教導了這一點。

在數據庫中它是關於事務,隔離級別和數據鎖定的。

典型(簡單)的競爭條件:

$visits = $pdo->query('SELECT visits FROM articles WHERE id = 44')->fetch()[0]['visits']; 
/* 
* do some time-consuming thing here 
* 
*/ 
$visits++; 
$pdo->exec('UPDATE articles SET visits = '.$visits.' WHERE id = 44'); 

上面競爭條件,如果2 PHP進程在另一個之後從數據庫中讀取一毫秒的訪問可以輕鬆地將酸,並假設初始訪問值爲6,兩者都會將其增加到7,並且都會將7寫回到數據庫中,即使期望的效果是2次訪問將值增加2(最終值訪問應該是8)。

解決方案是使用原子操作(因爲操作很簡單,可以簡化爲單個原子操作)。

UPDATE articles SET visits = visits+1 WHERE id = 44; 

原子操作由數據庫引擎保證不會被其他進程/線程中斷。通常,數據庫必須對傳入的更新進行排隊,以便它們不會相互影響。排隊顯然會減慢速度,因爲每個進程必須等待所有進程前面的進程,直到它有機會執行。

在操作較少簡單,我們需要一個以上的語句:

SELECT @visits := visits FROM articles WHERE ID = 44; 
SET @visits = @visits+1; 
UPDATE articles SET visits = @visits WHERE ID = 44; 

但同樣,即使在數據庫級別3個單獨的原子聲明並不能保證產生原子結果。它們可以與其他操作重疊。就像PHP示例一樣。

爲了解決這個問題,你必須做到以下幾點:

START TRANSACTION 
    SELECT @visits := visits FROM articles WHERE ID = 44 FOR UPDATE; 
    SET @visits = @visits+1; 
    UPDATE articles SET visits = @visits WHERE ID = 44; 
COMMIT;