MySQL 在高并发下的 订单撮合 系统使用 共享锁 与 排他锁 保证数据一致性
作者:林冠宏 / 指尖下的幽靈
掘金:https://juejin.im/user/587f0dfe128fe100570ce2d8
博客:http://www.cnblogs.com/linguanh/
GitHub : https://github.com/af913337456/
騰訊云專欄: https://cloud.tencent.com/developer/user/1148436/activities
蟲洞區(qū)塊鏈專欄:https://www.chongdongshequ.com/article/1536563643883.html
前序
距離上次擇文發(fā)表,兩月余久。2018年也即將要結(jié)束了,目前的工作依然是與區(qū)塊鏈應(yīng)用相關(guān)的,也很榮幸在9月初受邀簽約出版暫名為《區(qū)塊鏈以太坊DApp實(shí)戰(zhàn)開發(fā)》一書,預(yù)計(jì)在明年年初出版。
這次讓我有感記錄這篇文章的原因是最近在使用Go語言重寫一個(gè)原來由PHP語言編寫的交易所訂單撮合模塊的時(shí)候,發(fā)現(xiàn)訂單撮合的部分代碼在撮合的時(shí)候,為保證各表數(shù)據(jù)在并發(fā)情況下不出現(xiàn)讀寫臟亂而采用了全局鎖表的操作。后面我采用了共享鎖的形式進(jìn)行了修改,于剛剛重寫完,并進(jìn)行了并發(fā)單元測(cè)試,表現(xiàn)正常。
目錄
- 場(chǎng)景描述
- 解決問題
- 訂單撮合實(shí)例
- 共享鎖 與 排他鎖
- 前置知識(shí)
- 行鎖與表鎖
- 兩種行鎖的特點(diǎn)
- 兩種行鎖的加鎖方式
- 鎖的釋放
- 操作例子
- 改造代碼片段
場(chǎng)景描述
高并發(fā)的業(yè)務(wù)常見是有很多種類的,最常見的例如秒殺搶購。它們都有一個(gè)共同的特點(diǎn)就是數(shù)據(jù)更新都比較頻繁,通常涉及到多張業(yè)務(wù)表的增改操作,且表格越多的,要考慮的問題也越多。
訂單撮合可以理解為訂單買賣,拿這個(gè)為例子進(jìn)行列舉一個(gè)可能會(huì)導(dǎo)致數(shù)據(jù)錯(cuò)亂的情形。假設(shè)現(xiàn)在買賣手機(jī),A用戶是要買手機(jī)的,B用戶是賣手機(jī)的。A的買入單訂單1,和B的賣出單訂單2,訂單2賣出手機(jī),一臺(tái)手機(jī)賣1000元。此時(shí)A的網(wǎng)上的錢包余額是1001元,剛好比手機(jī)價(jià)格高,是可以成交的。
此時(shí)記錄用戶錢包錢數(shù)數(shù)量的是一張數(shù)據(jù)表。每次花費(fèi)了錢或者增加了錢,都要更新這個(gè)表。
當(dāng)這兩筆訂單進(jìn)入到系統(tǒng)里面進(jìn)行撮合。假設(shè)系統(tǒng)的訂單撮合運(yùn)行流程如下圖所示:
當(dāng)判斷條件進(jìn)行A用戶的錢包余額判斷的時(shí)候,發(fā)現(xiàn) 1001 > 1000,結(jié)果是通過,此時(shí)準(zhǔn)備進(jìn)入“進(jìn)行記錄更細(xì)”步驟。但是,就在這個(gè)過程之中的時(shí)間差中,A用戶使用了系統(tǒng)的網(wǎng)上提現(xiàn)功能,并成功轉(zhuǎn)出了10元,剩余的是1001 - 10 = 991元。但是由于撮合系統(tǒng)的余額判斷過程以及通過了,導(dǎo)致下面的交易流程依然能進(jìn)行,最終A用991元買了B的1000元售價(jià)的手機(jī)。
解決問題
上述的常見問題是一個(gè)很簡單的模型,現(xiàn)實(shí)的系統(tǒng)中往往是更復(fù)雜的。但是它所體現(xiàn)出的問題卻是真實(shí)存在的,對(duì)于這類問題,有很多解決方案。其中,就可以考慮使用數(shù)據(jù)庫的鎖。
本文要介紹的是MySQL數(shù)據(jù)庫的共享鎖 與 排他鎖,其它的不作說明或引申。
訂單撮合實(shí)例
下面的截圖就是我所重寫好的撮合系統(tǒng)原始的PHP代碼,所使用了表鎖的方式來解決前面的并發(fā)讀寫導(dǎo)致數(shù)據(jù)臟亂的問題。這種方式雖然是解決了問題,但是導(dǎo)致了性能低下的問題。
共享鎖 與 排他鎖
前置知識(shí):
- MySQL 是數(shù)據(jù)庫,不是數(shù)據(jù)庫引擎
- MySQL有兩種常用存儲(chǔ)引擎: MyISAM和InnoDB
- MyISAM不支持事務(wù)操作,InnoDB支持事務(wù)操作
- MySQL 的鎖分有 行鎖 和 表鎖
- MyISAM 只有表鎖
- Innodb 行鎖,表鎖都有
- 行鎖中有共享鎖和排他鎖
- 共享鎖 簡稱 S鎖,排他鎖簡稱 X鎖
行鎖與表鎖
簡述:
行鎖,鎖的是表中對(duì)應(yīng)的行,只限制當(dāng)前行的讀寫。
表鎖,鎖的是整張表,限制的是整張表的數(shù)據(jù)讀寫。
比較:
- 行鎖,計(jì)算機(jī)資源開銷大,加鎖慢;會(huì)出現(xiàn)死鎖;鎖定粒度最小,鎖沖突的概率最低,并發(fā)度最高,性能高。
- 表鎖,計(jì)算機(jī)資源開銷小,加鎖快;不會(huì)出現(xiàn)死鎖;鎖定粒度大,鎖沖突的概率最高,并發(fā)度最低,性能低。
兩種行鎖的特點(diǎn)
共享鎖
A 對(duì)數(shù)據(jù) B 加了 共享鎖,A能讀取和修改數(shù)據(jù)B,C 等其它只能讀取數(shù)據(jù)B,但是不能修改。直至A釋放了B的鎖。
排他鎖
A 對(duì)數(shù)據(jù) B 加了 排他鎖,A能讀取和修改數(shù)據(jù)B,C 等其它不能再對(duì)數(shù)據(jù)B加其它的鎖。直觀體驗(yàn)是不能修改,不能使用含有加鎖動(dòng)作的select讀取。
兩種行鎖的加鎖方式
要注意的是:
- 行鎖的實(shí)現(xiàn)SQL語句中必須要有索引的限制條件,例如含有 where id=xxx 這類語句。
- 行鎖的實(shí)現(xiàn)SQL語句沒有索引限制條件會(huì)變成表鎖
- InnoDB引擎 默認(rèn)的修改數(shù)據(jù)類SQL語句,update,delete,insert等,都會(huì)自動(dòng)給涉及到的數(shù)據(jù)加上排他鎖。
共享鎖
- select 的添加可以使用滿足格式:select ... where 索引限制 lock in share mode 的語句。例如“select name from lgh_user where id = 1 lock in share model” 此時(shí) id 是索引。
排他鎖
- 滿足格式:select ... where 索引限制 for update 的語句
鎖的釋放
非事務(wù)(Transaction) 中,語句執(zhí)行完畢,便釋放鎖。
行鎖在事務(wù) (Transaction) 中,只有等到當(dāng)前的事務(wù)Transaction 進(jìn)行了 commit 或 roll back,鎖才能釋放。
操作例子
演示事務(wù) tx 中的例子,文字解析見圖。
改造代碼片段
撮合中的所有表鎖替換成了共享鎖,運(yùn)行其它業(yè)務(wù)讀取所鎖的行數(shù)據(jù),在當(dāng)前事務(wù)的批量操作還沒結(jié)束之前,不允許修改。
完
總結(jié)
以上是生活随笔為你收集整理的MySQL 在高并发下的 订单撮合 系统使用 共享锁 与 排他锁 保证数据一致性的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: pecl安装扩展(首选)
- 下一篇: linux cmake编译源码,linu