mysql的事務和數據庫鎖的關系


數據庫加事務並不是數據就安全來了,事務和鎖要分析清楚和配合使用

問題背景處於對高並發的秒殺環節的理解整理如下:


秒殺的時候高並發主要注意
1、在秒殺的情況下,肯定不能如此高頻率的去讀寫數據庫,會嚴重造成性能問題的
必須使用緩存,將需要秒殺的商品放入緩存中,並使用鎖來處理其並發情況。當接到用戶秒殺提交訂單的情況下,先將商品數量遞減(加鎖/解鎖)后再進行其他方面的處理,處理失敗在將數據遞增1(加鎖/解鎖),否則表示交易成功。
當商品數量遞減到0時,表示商品秒殺完畢,拒絕其他用戶的請求。
2、這個肯定不能直接操作數據庫的,會掛的。直接讀庫寫庫對數據庫壓力太大,要用緩存。
把你要賣出的商品比如10個商品放到緩存中;然后在memcache里設置一個計數器來記錄請求數,這個請求書你可以以你要秒殺賣出的商品數為基數,比如你想賣出10個商品,只允許100個請求進來。那當計數器達到100的時候,后面進來的就顯示秒殺結束,這樣可以減輕你的服務器的壓力。然后根據這100個請求,先付款的先得后付款的提示商品以秒殺完。
3、首先,多用戶並發修改同一條記錄時,肯定是后提交的用戶將覆蓋掉前者提交的結果了。
這個直接可以使用加鎖機制去解決,樂觀鎖或者悲觀鎖。
樂觀鎖,就是在數據庫設計一個版本號的字段,每次修改都使其+1,這樣在提交時比對提交前的版本號就知道是不是並發提交了,但是有個缺點就是只能是應用中控制,如果有跨應用修改同一條數據樂觀鎖就沒辦法了,這個時候可以考慮悲觀鎖。
悲觀鎖,就是直接在數據庫層面將數據鎖死,類似於oralce中使用select xxxxx from xxxx where xx=xx for update,這樣其他線程將無法提交數據。
除了加鎖的方式也可以使用接收鎖定的方式,思路是在數據庫中設計一個狀態標識位,用戶在對數據進行修改前,將狀態標識位標識為正在編輯的狀態,這樣其他用戶要編輯此條記錄時系統將發現有其他用戶正在編輯,則拒絕其編輯的請求,類似於你在操作系統中某文件正在執行,然后你要修改該文件時,系統會提醒你該文件不可編輯或刪除。
4、不建議在數據庫層面加鎖,建議通過服務端的內存鎖(鎖主鍵)。當某個用戶要修改某個id的數據時,把要修改的id存入memcache,若其他用戶觸發修改此id的數據時,讀到memcache有這個id的值時,就阻止那個用戶修改。
5、實際應用中,並不是讓mysql去直面大並發讀寫,會借助“外力”,比如緩存、利用主從庫實現讀寫分離、分表、使用隊列寫入等方法來降低並發讀寫。

mysql
-----MyISAM引擎:
使用的是表級鎖。理解為鎖住整個表,可以同時讀,寫不行。

----innoDB
使用的是行級鎖,它也支持表級鎖。單獨的一行記錄加鎖 。
事務和數據庫鎖的關系如下

讀操作可以分成兩類:快照讀 (snapshot read)與當前讀 (current read)。
快照讀,讀取的是記錄的可見版本 (有可能是歷史版本),不用加鎖。
當前讀,讀取的是記錄的最新版本,並且,當前讀返回的記錄,都會加上鎖,保證其他事務不會再並發修改這條記錄。

鎖類型
共享鎖(S鎖):假設事務T1對數據A加上共享鎖,那么事務T2可以讀數據A,不能修改數據A。
排他鎖(X鎖):假設事務T1對數據A加上共享鎖,那么事務T2不能讀數據A,不能修改數據A。
我們通過update、delete等語句加上的鎖都是行級別的鎖。只有LOCK TABLE … READ和LOCK TABLE … WRITE才能申請表級別的鎖。


當執行select 的時候 默認是不加鎖的 (快照讀) (這種說法在隔離級別為Serializable中不成立)
如果想要對某個行數據加鎖需要 執行如下:
select * from table where num = 200 lock in share mode 共享鎖
select * from table where num = 200 for update 行級鎖
這是通過顯示加鎖實現的

當執行update,insert,delete的時候 默認是加行鎖的

Cluster Index:聚簇索引
InnoDB存儲引擎的數據組織方式,是聚簇索引表:完整的記錄,存儲在主鍵索引中,通過主鍵索引,就可以直接獲取記錄所有的列。


Read Uncommited(RU):讀未提交,一個事務可以讀到另一個事務未提交的數據!
Read Committed (RC):讀已提交,一個事務可以讀到另一個事務已提交的數據!
Repeatable Read (RR):可重復讀,加入間隙鎖,一定程度上避免了幻讀的產生!注意了,只是一定程度上,並沒有完全避免!我會在下一篇文章說明!另外就是記住從該級別才開始加入間隙鎖(這句話記下來,后面有用到)!
Serializable:串行化,該級別下讀寫串行化,且所有的select語句后都自動加上lock in share mode,即使用了共享鎖。因此在該隔離級別下,使用的是當前讀,而不是快照讀。
完全串行化的讀,每次讀都需要獲得表級共享鎖,讀寫相互都會阻塞
完全串行化的讀,每次寫都需要獲得所有行級鎖,讀寫相互都會阻塞

RC/RU+條件列非索引
select * from table where num = 200 lock in share mode 共享鎖
select * from table where num = 200 for update 行鎖

RR/Serializable+條件列非索引
select * from table where num = 200
在RR級別下,不加任何鎖,是快照讀。(可重讀的時候也不加鎖)
在Serializable級別下 ,完全串行化的讀,每次讀都需要獲得表級共享鎖,讀寫相互都會阻塞

select * from table where num = 200 lock in share mode
在Serializable級別下, 全表所有記錄都加共享鎖

select * from table where num = 200 for update
在Serializable級別下, 全表所有記錄都加上行鎖

所以:Serializable 都是表鎖


參見:https://www.cnblogs.com/rjzheng/p/9950951.html,https://blog.csdn.net/xf552527/article/details/78811262

以上是對事務和鎖的理解

參見:https://fengberlin.github.io/post/seckill/  高並發處理思路,https://blog.csdn.net/GallenZhang/article/details/78626730 

 


免責聲明!

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



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