摘要:做商城開發(fā)時經(jīng)常會遇到高并發(fā)的問題,除了使用Redis隊列等技術外,也可以使用Mysql數(shù)據(jù)庫的“鎖”機制。悲觀鎖:一般使用 select ...for update 對所選擇的數(shù)據(jù)進行加鎖處理,例如select * from yzm_order...
做商城開發(fā)時經(jīng)常會遇到高并發(fā)的問題,除了使用Redis隊列等技術外,也可以使用Mysql數(shù)據(jù)庫的“鎖”機制。
一、悲觀鎖
1、當事務在操作數(shù)據(jù)時把這部分數(shù)據(jù)進行鎖定,直到操作完畢后再解鎖,其他事務操作才可操作該部分數(shù)據(jù)。這將防止其他進程讀取或修改表中的數(shù)據(jù)。
2、實現(xiàn):
一般使用 select ...for update 對所選擇的數(shù)據(jù)進行加鎖處理,例如 SELECT * FROM `yzm_order` WHERE `order_no` = 'S201909277894' LIMIT 1 FOR UPDATE ,這樣就通過開啟排他鎖的方式實現(xiàn)了悲觀鎖。此時在 yzm_order 表中,order_no 為 S201909277894 的那條數(shù)據(jù)就被我們鎖定了,其它的事務必須等本次事務提交之后才能執(zhí)行。這樣我們可以保證當前的數(shù)據(jù)不會被其它事務修改。
二、樂觀鎖
1、如果有人在你之前更新了,你的更新應當是被拒絕的,可以讓用戶重新操作。
2、實現(xiàn):
大多數(shù)基于數(shù)據(jù)版本(Version)記錄機制實現(xiàn),
具體可通過給表加一個版本號或時間戳字段實現(xiàn),當讀取數(shù)據(jù)時,將version字段的值一同讀出,數(shù)據(jù)每更新一次,對此version值加一。當我們提交更新的時候,判斷當前版本信息與第一次取出來的版本值大小,如果數(shù)據(jù)庫表當前版本號與第一次取出來的version值相等,則予以更新,否則認為是過期數(shù)據(jù),拒絕更新,讓用戶重新操作。
在數(shù)據(jù)庫中,悲觀鎖的流程如下:
在對任意記錄進行修改前,先嘗試為該記錄加上排他鎖(exclusive locking)。
如果加鎖失敗,說明該記錄正在被修改,那么當前查詢可能要等待或者拋出異常。 具體響應方式由開發(fā)者根據(jù)實際需要決定。
如果成功加鎖,那么就可以對記錄做修改,事務完成后就會解鎖了。
其間如果有其他對該記錄做修改或加排他鎖的操作,都會等待我們解鎖或直接拋出異常。
測試一把:
會話1:
SET autocommit=0 SELECT * FROM yzm_order WHERE `order_no` = 'S201909277894' FOR UPDATE COMMIT
新會話2:
在會話1未commit之前,執(zhí)行以下SQL,會發(fā)生堵塞
UPDATE `yzm_order` SET `status` = 2 WHERE id = 1;
當會話1長時間未提交事務時,會發(fā)生以下報錯信息
[Err] 1205 - Lock wait timeout exceeded; try restarting transaction
再次測試:
例1: (明確指定主鍵,并且有此記錄,row lock)
SELECT * FROM yzm_order WHERE id='3' FOR UPDATE;
例2: (明確指定主鍵,且無此記錄,無lock)
SELECT * FROM yzm_order WHERE id='-1' FOR UPDATE;
例3: (無主鍵,table lock)
SELECT * FROM yzm_order WHERE status='1' FOR UPDATE;
例4: (主鍵不明確,table lock)
SELECT * FROM yzm_order WHERE id<>'3' FOR UPDATE;
例5: (主鍵不明確,table lock)
SELECT * FROM yzm_order WHERE id LIKE '3' FOR UPDATE;
總結:InnoDB默認行級鎖。行級鎖都是基于索引的,如果一條SQL語句用不到索引是不會使用行級鎖的,會使用表級鎖把整張表鎖住。
網(wǎng)友評論:
交換鏈接嗎
2019-10-22 09:13:36 回復
網(wǎng)友評論:
交換鏈接嗎
2019-07-29 21:33:14 回復
網(wǎng)友評論:
還有這種東西?
2019-07-29 16:05:30 回復