[英]Understanding pdo mysql transactions
PHP 文檔說:
如果您以前從未接觸過事務,它們提供了 4 個主要特性:原子性、一致性、隔離性和持久性 (ACID)。 通俗地說,在事務中執行的任何工作,即使是分階段執行的,在提交時都可以保證安全地應用於數據庫,並且不受其他連接的干擾。
問題:
這是否意味着我可以讓兩個單獨的 php 腳本同時運行事務而不會相互干擾?
詳細說明我所說的“干擾”是什么意思:
假設我們有以下employees
表:
__________________________
| id | name | salary |
|------+--------+----------|
| 1 | ana | 10000 |
|------+--------+----------|
如果我有兩個具有相似/相同代碼的腳本並且它們同時運行:
script1.php和script2.php (都具有相同的代碼):
$conn->beginTransaction();
$stmt = $conn->prepare("SELECT * FROM employees WHERE name = ?");
$stmt->execute(['ana']);
$row = $stmt->fetch(PDO::FETCH_ASSOC);
$salary = $row['salary'];
$salary = $salary + 1000;//increasing salary
$stmt = $conn->prepare("UPDATE employees SET salary = {$salary} WHERE name = ?");
$stmt->execute(['ana']);
$conn->commit();
並假設事件的順序如下:
script1.php選擇數據
script2.php選擇數據
script1.php更新數據
script2.php更新數據
script1.php commit() 發生
script2.php commit() 發生
在這種情況下,ana 的工資是多少?
會是11000嗎? 這是否意味着 1 個事務將與另一個事務重疊,因為信息是在任一提交發生之前獲得的?
會是12000嗎? 那么這是否意味着無論更新和選擇數據的順序如何, commit()
函數都會強制這些單獨發生?
請隨意詳細說明事務和單獨的腳本如何相互干擾(或不干擾)。
您不會在 php 文檔中找到答案,因為這與 php 或 pdo 無關。
mysql中的innodb表引擎提供了4個所謂的符合sql標准的隔離級別。 隔離級別與阻塞/非阻塞讀取結合將決定上述示例的結果。 您需要了解各種隔離級別的含義,並根據需要選擇合適的級別。
總結一下:如果你在關閉自動提交的情況下使用可序列化隔離級別,那么結果將是 12000。在所有其他隔離級別和啟用自動提交的可序列化級別中,結果將是 11000。如果你開始使用鎖定讀取,那么結果可能是在所有隔離級別下為 12000。
根據給定的條件(一個單獨的 DML 語句)判斷,這里不需要事務,而是需要表鎖。 這是一個非常常見的混淆。
如果您需要確保所有 DML 語句都正確執行或根本沒有執行,則需要一個事務。
手段
雖然,正如 Shadow 的優秀回答中所指出的,您可以在此處使用具有適當隔離級別的事務,但這會相當混亂。 您在這里需要的是表鎖定。 InnoDB 引擎允許您鎖定特定行而不是鎖定整個表,因此應該是首選。
如果您希望工資為 1200 - 則使用表鎖。
或者 - 一種更簡單的方法 - 只需運行原子更新查詢:
UPDATE employees SET salary = salary + 1000 WHERE name = ?
在這種情況下,所有工資都將被記錄。
如果你的目標不同,最好明確表達出來。
但同樣:您必須了解事務一般與單獨的腳本執行無關。 關於您的競爭條件主題,您不是對事務感興趣,而是對表/行鎖定感興趣。 這是一個非常常見的混淆,你最好直接學習:
事務和鎖定干擾的唯一主題是死鎖,但同樣 - 只有在事務使用鎖定的情況下。
唉,“無干擾”需要程序員的一些幫助。 它需要BEGIN
和COMMIT
來定義“事務”的范圍。 還有...
你的例子是不夠的。 第一條語句需要SELECT ... FOR UPDATE
。 這告訴事務處理對於SELECT
獲取的行可能會有一個UPDATE
。 該警告對於“防止干擾”至關重要。 現在時間線是這樣的:
FOR UPDATE
)(注意:這不是“死鎖”,只是“等待”。)
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.