做者:林冠宏 / 指尖下的幽靈html
博客:http://www.cnblogs.com/linguanh/github
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
代碼,所使用了表鎖
的方式來解決前面的併發讀寫致使數據髒亂的問題。這種方式雖然是解決了問題,可是致使了性能低下
的問題。
數據庫引擎
MyISAM
和InnoDB
MyISAM
不支持事務操做,InnoDB
支持事務操做行鎖
和 表鎖
共享鎖
和排他鎖
共享鎖
簡稱 S鎖,排他鎖
簡稱 X鎖簡述:
行鎖,鎖的是表中對應的行,只限制當前行的讀寫。
表鎖,鎖的是整張表,限制的是整張表的數據讀寫。
比較:
死鎖
;鎖定粒度最小,鎖衝突的機率最低,併發度
最高,性能高。死鎖
;鎖定粒度大,鎖衝突的機率最高,併發度
最低,性能低。共享鎖
A 對數據 B 加了 共享鎖,A能讀取和修改數據B,C 等其它只
能讀取
數據B,可是不能修改
。直至A釋放了B的鎖。
排他鎖
A 對數據 B 加了 排他鎖,A能讀取和修改數據B,C 等其它不能再對數據B加其它的鎖。直觀體驗是不能修改,不能使用含有加鎖動做的
select
讀取。
要注意的是:
where id=xxx
這類語句。表鎖
InnoDB引擎
默認的修改數據
類SQL語句,update
,delete
,insert
等,都會自動給涉及到的數據加上排他鎖。共享鎖
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 中的例子,文字解析見圖。
撮合中的全部表鎖
替換成了共享鎖
,運行其它業務讀取所鎖的行數據,在當前事務的批量操做還沒結束以前,不容許修改。