秒殺怎麼樣才能夠防止超賣?基於mysql的事務和鎖實現

Reference:  http://blog.ruaby.com/?p=256html

 

併發事務處理帶來的問題?

相對於串行處理來講,併發事務處理能大大增長數據庫資源的利用率,提升數據庫系統的事務吞吐量,從而能夠支持更多的用戶。但併發事務處理也會帶來一些問題,主要包括如下幾種狀況:mysql

  • 更新丟失(ost Update):當兩個或多個事務選擇同一行,而後基於最初選定的值更新該行時,因爲每一個事務都不知道其餘事務的存在,就會發生丟失更新問題--最後的更新覆蓋了由其餘事務所作的更新。例如,兩個編輯人員製做了同一文檔的電子副本。每一個編輯人員獨立地更改其副本,而後保存更改後的副本,這樣就覆蓋了原始文檔。最後保存其更改副本的編輯人員覆蓋另外一個編輯人員所作的更改。若是在一個編輯人員完成並提交事務以前,另外一個編輯人員不能訪問同一文件,則可避免此問題。
  • 髒讀(Dirty Reads):一個事務正在對一條記錄作修改,在這個事務完成並提交前,這條記錄的數據就處於不一致狀態;這時,另外一個事務也來讀取同一條記錄,若是不加控制,第二個事務讀取了這些「髒」數據,並據此作進一步的處理,就會產生未提交的數據依賴關係。這種現象被形象地叫作」髒讀」。
  • 不可重複讀(Non-Repeatabe Reads):一個事務在讀取某些數據後的某個時間,再次讀取之前讀過的數據,卻發現其讀出的數據已經發生了改變、或某些記錄已經被刪除了!這種現象就叫作「不可重複讀」。
  • 幻讀(Phantom Reads):一個事務按相同的查詢條件從新讀取之前檢索過的數據,卻發現其餘事務插入了知足其查詢條件的新數據,這種現象就稱爲「幻讀」。

事務隔離級別

在上面講到的併發事務處理帶來的問題中,「更新丟失」一般是應該徹底避免的。但防止更新丟失,並不能單靠數據庫事務控制器來解決,須要應用程序對要更新的數據加必要的鎖來解決,所以,防止更新丟失應該是應用的責任。git

「髒讀」、「不可重複讀」和「幻讀」,其實都是數據庫讀一致性問題,必須由數據庫提供必定的事務隔離機制來解決。數據庫實現事務隔離的方式,基本上可分爲如下兩種:github

  • 一種是在讀取數據前,對其加鎖,阻止其餘事務對數據進行修改。
  • 另外一種是不用加任何鎖,經過必定機制生成一個數據請求時間點的一致性數據快照(Snapshot),並用這個快照來提供必定級別(語句級或事務級)的一致性讀取。從用戶的角度來看,好象是數據庫能夠提供同一數據的多個版本,所以,這種技術叫作數據多版本併發控制(MutiVersion Concurrency Contro,簡稱MVCC或MCC),也常常稱爲多版本數據庫。

數據庫的事務隔離越嚴格,併發反作用越小,但付出的代價也就越大,由於事務隔離實質上就是使事務在必定程度上 「串行化」進行,這顯然與「併發」是矛盾的。同時,不一樣的應用對讀一致性和事務隔離程度的要求也是不一樣的,好比許多應用對「不可重複讀」和「幻讀」並不敏感,可能更關心數據併發訪問的能力。sql

InnoDB的行鎖模式及加鎖方法

InnoDB實現瞭如下兩種類型的行鎖。數據庫

  • 共享鎖(S):容許一個事務去讀一行,阻止其餘事務得到相同數據集的排他鎖。
  • 排他鎖(X):容許得到排他鎖的事務更新數據,阻止其餘事務取得相同數據集的共享讀鎖和排他寫鎖。

對於UPDATE、DELETE和INSERT語句,InnoDB會自動給涉及數據集加排他鎖(X);對於普通SELECT語句,InnoDB不會加任何鎖;事務能夠經過如下語句顯示給記錄集加共享鎖或排他鎖。併發

  • 共享鎖(S):SELECT * FROM table_name WHERE … LOCK IN SHARE MODE。
  • 排他鎖(X):SELECT * FROM table_name WHERE … FOR UPDATE。

用SELECT … IN SHARE MODE得到共享鎖,主要用在須要數據依存關係時來確認某行記錄是否存在,並確保沒有人對這個記錄進行UPDATE或者DELETE操做。可是若是當前事務也須要對該記錄進行更新操做,則頗有可能形成死鎖,對於鎖定行記錄後須要進行更新操做的應用,應該使用SELECT… FOR UPDATE方式得到排他鎖。htm

更多詳情參考:http://www.cnblogs.com/qq78292959/archive/2013/01/30/2883100.htmlblog

github項目地址:https://github.com/sanjiOP/seckill事務

秒殺系統的實現

按照正常的購買流程:查詢商品庫存,庫存大於0時,生成訂單,去庫存。若是出現併發,致使在查詢商品庫存的時候,庫存會一直出現大於0的狀況,出現超賣現象。

基於mysql的事務和鎖實現方式:

  • 1:開啓事務
  • 2:查詢庫存,並顯示的設置寫鎖(排他鎖):SELECT * FROM table_name WHERE … FOR UPDATE
  • 3:生成訂單
  • 4:去庫存,隱示的設置寫鎖(排他鎖):UPDATE goods SET counts = counts – 1 WHERE id = 1
  • 5:commit,釋放鎖

注意:

若是不開啓事務,第二步即便加鎖,第一個會話讀庫存結束後,變會釋放鎖,第二個會話仍有機會在去庫存前讀庫存,出現超賣。

若是開啓事務,第二步不加鎖,第一個會話讀庫存結束後,第二個會話容易出現【髒讀】,出現超賣。

即加事務,又加讀鎖:開啓事務,第一個會話讀庫存時加讀鎖,併發時,第二個會話也容許得到讀庫存的讀鎖,可是在第一個會話執行寫操做時,寫鎖便會等待第二個會話的讀鎖,第二個會話執行寫操做時,寫鎖便會等待第一個會話的讀鎖,出現死鎖

即加事務,又加寫鎖:第一個會話讀庫存時加寫鎖,寫鎖會阻止其它事務的讀鎖和寫鎖。直到commit纔會釋放,容許第二個會話查詢庫存,不會出現超賣現象。

相關文章
相關標籤/搜索