MySQL 在高並發下的 訂單撮合 系統使用 共享鎖 與 排他鎖 保證數據一致性


作者:林冠宏 / 指尖下的幽靈

掘金:https://juejin.im/user/587f0dfe128fe100570ce2d8

博客:http://www.cnblogs.com/linguanh/

GitHub : https://github.com/af913337456/

騰訊雲專欄: https://cloud.tencent.com/developer/user/1148436/activities

蟲洞區塊鏈專欄:https://www.chongdongshequ.com/article/1536563643883.html


前序

距離上次擇文發表,兩月余久。2018年也即將要結束了,目前的工作依然是與區塊鏈應用相關的,也很榮幸在9月初受邀簽約出版暫名為《區塊鏈以太坊DApp實戰開發》一書,預計在明年年初出版。

這次讓我有感記錄這篇文章的原因是最近在使用Go語言重寫一個原來由PHP語言編寫的交易所訂單撮合模塊的時候,發現訂單撮合的部分代碼在撮合的時候,為保證各表數據在並發情況下不出現讀寫臟亂而采用了全局鎖表的操作。后面我采用了共享鎖的形式進行了修改,於剛剛重寫完,並進行了並發單元測試,表現正常。

目錄

  • 場景描述
  • 解決問題
  • 訂單撮合實例
  • 共享鎖 與 排他鎖
    • 前置知識
    • 行鎖與表鎖
    • 兩種行鎖的特點
    • 兩種行鎖的加鎖方式
    • 鎖的釋放
    • 操作例子
  • 改造代碼片段

場景描述

高並發的業務常見是有很多種類的,最常見的例如秒殺搶購。它們都有一個共同的特點就是數據更新都比較頻繁,通常涉及到多張業務表的增改操作,且表格越多的,要考慮的問題也越多。

訂單撮合可以理解為訂單買賣,拿這個為例子進行列舉一個可能會導致數據錯亂的情形。假設現在買賣手機,A用戶是要買手機的,B用戶是賣手機的。A的買入單訂單1,和B的賣出單訂單2,訂單2賣出手機,一台手機賣1000元。此時A的網上的錢包余額是1001元,剛好比手機價格高,是可以成交的。

此時記錄用戶錢包錢數數量的是一張數據表。每次花費了錢或者增加了錢,都要更新這個表。

當這兩筆訂單進入到系統里面進行撮合。假設系統的訂單撮合運行流程如下圖所示:

當判斷條件進行A用戶的錢包余額判斷的時候,發現 1001 > 1000,結果是通過,此時准備進入“進行記錄更細”步驟。但是,就在這個過程之中的時間差中,A用戶使用了系統的網上提現功能,並成功轉出了10元,剩余的是1001 - 10 = 991元。但是由於撮合系統的余額判斷過程以及通過了,導致下面的交易流程依然能進行,最終A用991元買了B的1000元售價的手機。

解決問題

上述的常見問題是一個很簡單的模型,現實的系統中往往是更復雜的。但是它所體現出的問題卻是真實存在的,對於這類問題,有很多解決方案。其中,就可以考慮使用數據庫的鎖。

本文要介紹的是MySQL數據庫共享鎖排他鎖,其它的不作說明或引申。

訂單撮合實例

下面的截圖就是我所重寫好的撮合系統原始的PHP代碼,所使用了表鎖的方式來解決前面的並發讀寫導致數據臟亂的問題。這種方式雖然是解決了問題,但是導致了性能低下的問題。

共享鎖 與 排他鎖

前置知識:

  • MySQL 是數據庫,不是數據庫引擎
  • MySQL有兩種常用存儲引擎: MyISAMInnoDB
  • MyISAM不支持事務操作,InnoDB支持事務操作
  • MySQL 的鎖分有 行鎖表鎖
  • MyISAM 只有表鎖
  • Innodb 行鎖,表鎖都有
  • 行鎖中有共享鎖排他鎖
  • 共享鎖 簡稱 S鎖,排他鎖簡稱 X鎖

行鎖與表鎖

簡述:

  • 行鎖,鎖的是表中對應的行,只限制當前行的讀寫。

  • 表鎖,鎖的是整張表,限制的是整張表的數據讀寫。

比較:

  • 行鎖,計算機資源開銷大,加鎖慢;會出現死鎖;鎖定粒度最小,鎖沖突的概率最低,並發度最高,性能高。
  • 表鎖,計算機資源開銷小,加鎖快;不會出現死鎖;鎖定粒度大,鎖沖突的概率最高,並發度最低,性能低。

兩種行鎖的特點

共享鎖

A 對數據 B 加了 共享鎖,A能讀取和修改數據B,C 等其它只能讀取數據B,但是不能修改。直至A釋放了B的鎖。

排他鎖

A 對數據 B 加了 排他鎖,A能讀取和修改數據B,C 等其它不能再對數據B加其它的鎖。直觀體驗是不能修改,不能使用含有加鎖動作的select讀取。

兩種行鎖的加鎖方式

要注意的是:

  • 行鎖的實現SQL語句中必須要有索引的限制條件,例如含有 where id=xxx 這類語句。
  • 行鎖的實現SQL語句沒有索引限制條件會變成表鎖
  • InnoDB引擎 默認的修改數據類SQL語句,update,delete,insert等,都會自動給涉及到的數據加上排他鎖。

共享鎖

  • select 的添加可以使用滿足格式:select ... where 索引限制 lock in share mode 的語句。例如“select name from lgh_user where id = 1 lock in share model” 此時 id 是索引。

排他鎖

  • 滿足格式:select ... where 索引限制 for update 的語句

鎖的釋放

  • 非事務(Transaction) 中,語句執行完畢,便釋放鎖。

  • 行鎖在事務 (Transaction) 中,只有等到當前的事務Transaction 進行了 commit 或 roll back,鎖才能釋放。

操作例子

演示事務 tx 中的例子,文字解析見圖。

改造代碼片段

撮合中的所有表鎖替換成了共享鎖,運行其它業務讀取所鎖的行數據,在當前事務的批量操作還沒結束之前,不允許修改。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM