首頁  >  文章  >  後端開發  >  PHP中實作MySQL巢狀事務的兩種解決方案

PHP中實作MySQL巢狀事務的兩種解決方案

WBOY
WBOY原創
2016-08-08 09:25:19994瀏覽

一、問題起源

在MySQL的官方文件中有明確的說明不支援巢狀事務:

1. Transactions cannot be nested. This is a consequence of the implicit commit performed for any current transaction when you issue a START TRANSACTION statement or one of its synonyms. 

但是在我們開發一個複雜的系統時難免會無意中在事務中嵌套了事務,比如A函數調用了B函數,A函數使用了事務,並且是在事務中調用了B函數,B函數也有一個事務,這樣就出現了事務嵌套。這時候其實A的事務就意義不大了,為什麼呢?上面的文件就有提到,簡單的翻譯過來就是:

1. 執行一個START TRANSACTION指令時,會隱式的執行一個commit操作。 所以我們就要在系統架構層面來支援事務的嵌套。

所幸的是在一些成熟的ORM框架中都做了對嵌套的支持,例如doctrine或laravel。接下來我們就一起來看下這兩個框架是怎麼實現的。友情提示,這兩個框架的函數和變數的命名都比較的直觀,雖然看起來很長,但是都是透過命名就能直接得知這個函數或變數的意思,所以不要一看到那麼一大坨就被嚇到了:)

二、doctrine的解決方案

首先來看下在doctrine中建立交易的程式碼(幹掉了不相關的程式碼):

[php] view plaincopy

  1. /**  
  2. * 作者 http://www.lai18.com  
  3. * 日期 2015-04-19  
  4. * 版本 1  
  5. **/   
  6. public function beginTransaction()  
  7.   
  8. {  
  9.   
  10.     ++$this->_transactionNestingLevel;  
  11.   
  12.     if ($this->_transactionNestingLevel == 1) {  
  13.            $this->_conn->beginTransaction();  
  14.   
  15.     } else if ($this->_nestTransactionsWithSavepoints) {  
  16.   
  17.         
  18. $this->createSavepoint($this
  19. ->_getNestedTransactionSavePointName());  

🎜🎜  🎜🎜 🎜🎜    }  🎜🎜 🎜🎜  🎜🎜 🎜🎜}   🎜🎜 🎜🎜 🎜這個函數的第一行用一個_transactionNestingLevel來標識目前嵌套的級別,如果是1,也就是還沒有嵌套,那就用預設的方法執行一下START TRANSACTION就ok了,如果大於1,也就是有嵌套的時候,她會幫我們創建一個savepoint,這個savepoint可以理解為一個事務記錄點,當需要回滾時可以只回滾到這個點。然後看rollBack函數:🎜 🎜 🎜 🎜[php] view plaincopy 🎜 🎜
  1.     1. /**  
  2. * 作者 http://www.lai18.com  
  3. * 日期 2015-04-19  
  4. * 版本 1  
  5. **/   
  6. 公共 函數 rollBack()  
  7.   
  8. {  
  9.   
  10.     if ($this->_transactionNestingLevel == 0) {  
  11.            拋出 ConnectionException::noActiveTransaction();  
  12.   
  13.     }  
  14.     
  15. if ($this
  16. ->_transactionNestingLevel == 1) {  
  17.            $這個->_transactionNestingLevel = 0;  
  18.   
  19.         $這個->_conn->rollback();     
  20.         $這個
  21. ->_isRollbackOnly = false;  
  22.   
  23.     } else
  24.  
  25. if ($this->_nestTransactionsWithSavepoints) {     
  26.         $this
  27. ->rollbackSavepoint(
  28. $this->_getNestedTransactionSavePointName());  
  29.   
  30.         --
  31. $這個
  32. ->_transactionNestingLevel;  

  

    } 

否則 {  🎜🎜🎜🎜 🎜🎜  🎜🎜 🎜🎜        🎜$這個🎜->_isRollbackOnly = true;  🎜🎜🎜🎜 🎜🎜  🎜🎜 🎜🎜        --🎜$這個🎜->_transactionNestingLevel;  🎜🎜🎜🎜 🎜🎜  🎜🎜 🎜🎜    }  🎜🎜 🎜🎜  🎜🎜 🎜🎜}   🎜🎜 🎜🎜 🎜可以看處理的方式也很簡單,如果level是1,則直接回滾,否則就回滾到前面的保存點。然後我們繼續看下commit函數:🎜 🎜 🎜 🎜[php] 查看純文字 🎜 🎜
  1.     1. /**  
  2. * 作者 http://www.lai18.com  
  3. * 日期 2015-04-19  
  4. * 版本 1  
  5. **/   
  6. public function commit()  
  7.   
  8. {  
  9.   
  10.     if ($this->_transactionNestingLevel == 0) {  
  11.   
  12.         提交 ConnectionException::noActiveTransaction();  
  13.   
  14.     }  
  15.   
  16.     
  17. if ($this
  18. ->_isRollbackOnly) {  
  19.            提交 ConnectionException::commitFailedRollbackOnly();  
  20.   
  21.     }  
  22.     
  23. if ($this
  24. ->_transactionNestingLevel == 1) {  
  25.   
  26.         $這個
  27. ->_conn->commit();  
  28.        } else
  29.  
  30. if ($this
  31. ->_nestTransactionsWithSavepoints) {  
  32.   
        

$this

->releaseSavepoint(

$this

->_getNestedTransactionSavePointName());  

  1.   
  2.     }  
  3.     --$這個
  4. ->_transactionNestingLevel;  
  5.   
  6. }   
  7. 算了,不費口舌解釋清楚吧:) 三、laravel的解決方案laravel的處理方式相對簡單粗暴一些,我們先來看下創建事務的操作:
  8. [php] 查看純文字
  9.     1.    /**  
  10. * 作者 http://www.lai18.com  
  11. * 日期 2015-04-19  
  12. * 版本 1   **/
  13.    
  14. public function beginTransaction()  
  15.   
  16. {  
  17.        ++$這個
  18. ->交易;  
  19.     if
  20.  (
  21. $這個->交易 == 1)  
  

    {  

🎜  🎜🎜 🎜🎜        🎜$this🎜->pdo->beginTransaction();  🎜🎜🎜🎜 🎜🎜  🎜🎜 🎜🎜    }  🎜🎜 🎜🎜  🎜🎜 🎜🎜}   🎜🎜 🎜🎜 🎜感覺怎麼樣?的操作:🎜 🎜 🎜 🎜[php] 查看純文字 🎜 🎜
  1.     1. /**  
  2. * 作者 http://www.lai18.com  
  3. * 日期 2015-04-19  
  4. * 版本 1  
  5. **/   
  6. public function rollBack()  
  7.   
  8. {  
  9.   
  10.     if ($this->transactions == 1)  
  11.   
  12.     {  
  13.   
  14.         $this->transactions = 0;  
  15.         $this->pdo->rollBack();  
  16.   
  17.     }  
  18.   
  19.     else  
  20.   
  21.     {  
  22.   
  23.         --$this->transactions;  
  24.   
  25.     }  
  26.   
  27. }   

明白了吧?只有噹噹前事務只有一個的時候才會真正的rollback,否則只是將計數做減一操作。這也就是為啥剛才說laravel的處理比較簡單粗暴一些,在嵌套的內層裡面實際上是木有真正的事務的,只有最外層一個整體的事務,雖然簡單粗暴,但是也解決了在內層新建一個事務時會造成commit的問題。原理就是這個樣子了,為了保持完整起見,把commit的程式碼也copy過來吧!

[php] view plaincopy

  1. public function commit()  
  2.   
  3. {  
  4.   
  5.     if ($this->transactions == 1) $this->pdo->commit();      --$this->transactions;  
  6.    }  
  7. 以上就介紹了PHP中實作MySQL巢狀事務的兩種解決方案,包含了面向的內容,希望對PHP教學有興趣的朋友有幫助。
陳述:
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn