既然你不使用事務,那麼沒有的不會等待一個腳本中的所有查詢完成,因此查詢可能會重疊。
有一個叫做併發編程的完整研究領域,教導了這一點。
在數據庫中它是關於事務,隔離級別和數據鎖定的。
典型(簡單)的競爭條件:
$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;