簡體   English   中英

了解 pdo mysql 事務

[英]Understanding pdo mysql transactions

PHP 文檔說:

如果您以前從未接觸過事務,它們提供了 4 個主要特性:原子性、一致性、隔離性和持久性 (ACID)。 通俗地說,在事務中執行的任何工作,即使是分階段執行的,在提交時都可以保證安全地應用於數據庫,並且不受其他連接的干擾。

問題:

這是否意味着我可以讓兩個單獨的 php 腳本同時運行事務而不會相互干擾?


詳細說明我所說的干擾”是什么意思

假設我們有以下employees表:

 __________________________
|  id  |  name  |  salary  |
|------+--------+----------|
|  1   |  ana   |   10000  |
|------+--------+----------|

如果我有兩個具有相似/相同代碼的腳本並且它們同時運行:

script1.phpscript2.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 語句都正確執行或根本沒有執行,則需要一個事務

手段

  • 您不需要任何數量的 SELECT 查詢的事務
  • 如果只執行一個 DML 語句,則不需要事務

雖然,正如 Shadow 的優秀回答中所指出的,您可以在此處使用具有適當隔離級別的事務,但這會相當混亂。 您在這里需要的是表鎖定 InnoDB 引擎允許您鎖定特定行而不是鎖定整個表,因此應該是首選。

如果您希望工資為 1200 - 則使用表鎖。

或者 - 一種更簡單的方法 - 只需運行原子更新查詢:

UPDATE employees SET salary = salary + 1000 WHERE name = ?

在這種情況下,所有工資都將被記錄。

如果你的目標不同,最好明確表達出來。

但同樣:您必須了解事務一般與單獨的腳本執行無關。 關於您的競爭條件主題,您不是對事務感興趣,而是對表/行鎖定感興趣。 這是一個非常常見的混淆,你最好直接學習:

  • 事務是為了確保一個腳本中的一組DML查詢被成功執行。
  • 表/行鎖定是為了確保其他腳本執行不會干擾。

事務和鎖定干擾的唯一主題是死鎖,但同樣 - 只有在事務使用鎖定的情況下。

唉,“無干擾”需要程序員的一些幫助。 它需要BEGINCOMMIT來定義“事務”的范圍。 還有...

你的例子是不夠的。 第一條語句需要SELECT ... FOR UPDATE 這告訴事務處理對於SELECT獲取的行可能會有一個UPDATE 該警告對於“防止干擾”至關重要。 現在時間線是這樣的:

  • script1.php 開始
  • script2.php 開始
  • script1.php 選擇數據( FOR UPDATE
  • script2.php選擇數據被阻塞,所以等待
  • script1.php 更新數據
  • script1.php commit() 發生
  • script2.php 選擇數據(並將獲得新提交的值)
  • script2.php 更新數據
  • script2.php commit() 發生

(注意:這不是“死鎖”,只是“等待”。)

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM