首頁 >資料庫 >mysql教程 >資料庫中的事務和並發問題的實例講解

資料庫中的事務和並發問題的實例講解

零下一度
零下一度原創
2017-07-03 16:10:391366瀏覽

資料庫中的交易和並發問題探討

引子

最近有同事寫了段程式碼,負責建立訂單的邏輯,程式碼審查時發現可能會有並發的問題。同事不認同,他認為他的邏輯是寫在預存程序中的,應該沒有問題。

程式碼的邏輯大概是(偽代碼):

begin transaction

if 查询到客户存在进行中的订单
 rollback transaction

if 查询到设备存在进行中的订单
 rollback transaction

插入订单

commit transaction

 

#下面針對這個邏輯進行分析,為什麼這個交易會出現並發問題。

事務概述

首先,提出兩個問題,然後帶著問題討論事務相關的知識點,最後來解決這兩個問題並回答前文的問題。

第一個問題,事務是否可以並發?

第二個問題,資料庫是怎麼隔離交易的?

交易的表現特性

資料庫中執行事務涉及到很多方面,包括如何處理臨界資源,如何加鎖解鎖等等。但無論事務如何執行,都需要確保以下幾個特性:

  • 原子性

  • 一致性

  • 隔離性

  • 持久性

  • 原子性:所有的操作都是一個邏輯單元,要嘛都提交成功,要嘛就失敗;

    一致性:只有合法的資料被寫入資料庫,否則交易回滾到最初的狀態;

    隔離:允許多個交易同時進行,而不會破壞資料的正確性和完整性;

    持久性:事務結束後,已經提交的結果被固化保存。

    資料庫的各種鎖定

  1. 共享鎖定

#共享鎖定用於非獨佔的業務,允許多個事務同時讀取鎖定的資源,但不允許資源被更新。

  • 加上鎖定時機:執行select語句時預設會被加上

  • #解鎖時機:執行完讀取後預設解除

  • #與其他鎖定相容性:資料上被設定了共享鎖定,則不會允許再增加共享鎖定和獨佔鎖定

  • 並發效能:具有良好的並發性能

    1. 排他鎖

    #排他鎖,也叫獨佔鎖。顧名思義,被排他鎖鎖定的資源不會允許其他事務進行任何操作。

  • 加上鎖定時機:執行insert,update,delete時預設會加上

  • 解鎖時機:交易結束才能解除

  • 相容性:如果資料上有其他鎖,不能增加獨佔鎖;同樣獨佔鎖存在時也不會允許增加其他鎖

  • 並發效能:其他交易必須等待前一個交易結束後才能執行,不能並發,只能串列

    1. 更新鎖定

    在更新的初始階段用來鎖定所需的資源,防止在讀取階段使用共用鎖定造成死鎖。

  • 加鎖時機:執行update時,使用更新鎖定鎖定相關資源

  • #解鎖時機:讀取完畢,執行更新作業時,更新鎖定升級為獨佔鎖定

  • 相容性:更新鎖定與共用鎖定相容,即可同時存在更新鎖定和共用鎖定,但只能有一個更新鎖定

  • #並發效能:更新初期的讀取階段可以允許其他事務讀取資源,允許有限的並發;後期對資源進行獨佔時不允許並發。

  • 事務隔離級別

    通用的事務隔離級別有四種,SQL Server還有另外擴展出來的級別,在此不多介紹。

    1. Serializable(序列化)

    #工作方式類似於重複讀取。但它不僅會鎖定受影響的數據,還會鎖定這個範圍。這阻止了新資料插入查詢所涉及的範圍,這種情況可以導致幻像讀取。

    1. Repeatable Read(可重複讀)

    #像已提交讀取層級一樣讀取數據,但會保持共享鎖定直到交易結束。

    1. Read Commit

    只讀取提交的資料並等待其他交易釋放排他鎖定。讀取資料的共用鎖定在讀取操作完成後立即釋放。已提交讀取是SQL Server的預設隔離等級。

    1. Read Uncommited

    在讀取資料時不會檢查或使用任何鎖定。因此,在這種隔離等級中可能讀取到沒有提交的資料。

    回答前文的問題

    第一個問題,事務是否可以並發?

    答案是肯定的,資料庫中為了提高效能,允許同時進行多個事務操作,這個事務跟發起方式無關,使用預存程序發起,或是使用程式碼發起,又或者使用普通的SQL語句發起並沒有什麼差別。

    第二個問題,資料庫是怎麼隔離交易的?

    要回答這個問題,先要理解資料庫中的鎖定機制和資料庫事務隔離等級。資料庫中的鎖可以分為三種類型:共享鎖、獨佔鎖和更新鎖。使用不同層級的鎖定並配合不同的鎖定範圍已達到不同的交易隔離等級並在此基礎上並發或序列執行交易。

    第三個問題,為什麼本文開頭的交易會出現並發問題?

    因為事務的開始執行的是select,select使用的是共享鎖,有可能並發的事務在同一時間執行select導致同時認為自己都是合法操作,而排隊執行後續的事務。結果導致了實際上就有可能插入重複的數據,例如只剩下一個商品,卻創建了兩個銷售訂單。

    如何防止並發問題

    1. 在事務中

    根據前文所講,使用insert,update或delete可以在預設事務等級人為造成事務串列化,因此可以在事務內部一開始都使用update更新一條公共的數據,這樣的話同類型的事務都會串行化,然後再增加一個判斷語句,用於判斷後續的事務內容是否應該執行。這樣足以確保所有的操作都按照合理合法,唯一的缺點是可能造成性能問題。

    1. 在事務外

    現在分散式的系統越來越多,但是再分散的系統也會有些共享資源,例如redis或zookeeper,可以利用redis或zookeeper來造一些分散式的鎖(這類屬於其他博文內容,在此不再展開)。利用事務外部的鎖定將同類型的事務做一些串行化處理,再配合事務內部的檢查機制,足以確保解決事務的並發問題。

    參考資料

  • 事務並發的問題及處理

  • 資料庫事務的四大特性以及交易的隔離等級

  • 資料庫交易與並發

  • SQLServer交易的隔離等級


  • #

    以上是資料庫中的事務和並發問題的實例講解的詳細內容。更多資訊請關注PHP中文網其他相關文章!

    陳述:
    本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn