mysql的鎖和存取錢
2024-07-24 12:40:16
供稿:網友
在處理鎖的問題上,經常聽到:共享鎖、排它鎖、悲觀鎖、樂觀鎖、行級鎖、表級鎖。
共享鎖: 就是在讀取數據的時候,給數據添加一個共享鎖。共享和共享直接是不沖突的,但是和排他鎖是沖突的。
排他鎖: 更新數據的時候,安裝排他鎖,禁止其他一切行為。
場 景:老公去在 ATM 上取錢,老婆在柜臺存錢,假設這個賬戶中有 1000 元。老公首先執(zhí)行查詢操作,查詢到賬戶余額為 1000 此時程序 將 1000 拿到內存中,老公取了200 元,程序就執(zhí)行了更新操作將賬戶余額改為 800,但是當老公的程序沒有 commit 的時候,老婆查詢賬戶,此時賬戶余額還是 1000 元,老婆存入 200 元,程序執(zhí)行了更新操作將賬戶余額改為 1200,然后老公將更新語句提交,接著老婆也將更新語句提交。最后導致的結果就是該賬戶的余額為 1200,這就是更新丟失的問題。引發(fā)更新丟失的根源就是查詢上,因為雙方都是根據從數據庫查詢到的數據再對數據庫中的數據進行更新的。
解決更新丟失有三個方案:
(1) 將事務隔離級別設置為最高,采用死鎖策略。
(2) 采用悲觀鎖,悲觀鎖不是數據庫中真正的鎖,是人們看待事務的態(tài)度。
(3) 采用樂觀鎖,樂觀鎖也不是數據庫中真正的鎖。
如 果我們采用的是第一個方案時,老公進行查詢操作,數據庫為表增加了共享鎖,老婆進行查詢操作時數據庫也增加了一個共享鎖。但是當老公進行更新數據庫操作 時,由于老婆拿著共享鎖,導致老公不能增加排它鎖,老婆進行更新操作時,因為老公拿著共享鎖,導致老婆也拿不到排它鎖,這就發(fā)生了死鎖現(xiàn)象,你等我,我等你。在 mysql 中,處理死鎖的方案是釋放掉一方的鎖。這樣就保證了一方更新成功,但是這種性能極低,因為數據庫頻繁在解決死鎖問題。
悲觀鎖(更新多,查詢少時用)
如果我們采用的是第二個方案時,即采用悲觀鎖。就是我們在操作數據庫時采用悲觀的態(tài)度,認為別人會在此時并發(fā)訪問數據庫。
我們在查詢語句中 select * from account where name='aaa' for update; 等于加了排它鎖。
當老公查詢余額的時候,select money from account where name='aaa' for update; 增加了排它鎖,
老婆查詢賬戶余額的時候, select money from account where name='aaa' for update; 也要求對數據庫加排它鎖,
因為老公已經拿到了排它鎖,導致老婆不能加鎖,所以老婆只有等待老公執(zhí)行完畢,釋放掉鎖以后才能繼續(xù)操作。
樂觀鎖(更新少,查詢多時用)
如 果我們采用的是第三個方案時,即采用樂觀鎖,就是我們在操作數據庫的時候會認為沒有其它用戶并發(fā)訪問,但是樂觀鎖也不是完全樂觀的,樂觀鎖是采用版本號的 方式進行控制的。在數據庫表中有一列版本號。從數據庫中查詢的時候,將版本號也查詢過來,在進行更新操作的時候,將版本號加1,查詢條件的版本號還是查詢過來的版本號。
比如:
老公執(zhí)行查詢操作
select money,version from account where name='aaa';
假設此時查詢到的版本號為 0,
老公在進行更新操作
update account set money=money+100,version=version+1 where name='aaa' and version=0;
未提交時老婆來查詢,查詢到的版本號依然是 0,
老婆也執(zhí)行更新操作
update account set money=money+100,version=version+1 where name='aaa' and version=0;
現(xiàn)在老公提交了事務,老婆再提交事務的時候發(fā)現(xiàn)版本號為 0 的記錄沒有了,所以就避免了數據丟失的問題。不過這種情況也導致了多個用戶更新操作時,只有一個用戶的更新被執(zhí)行。
行級別的鎖:
select * from employee where employeeID=9857 for update; where 后邊是索引列 不是索引列那么就為表級別的鎖