悲觀鎖,樂觀鎖,排他鎖,行鎖----MYSQL
在說具體的鎖結構時,先思考一個問題,那就是為什麼要上鎖?然後我要如何選擇鎖?鎖具體如何實現?
在文章得末尾我給出了我的個人答案。
一、什麼是悲觀鎖?
1、悲觀鎖就是在操作資料時,認為此操作會出現資料衝突,所以在進行每次操作時都要通過獲取鎖才能進行對相同資料的操作,這點跟java中的synchronized很相似。
2、在MySQL中如何實現悲觀鎖。?
mysql中有悲觀鎖的實現,我們想實現悲觀鎖時呼叫相對應得語句。
測試用表的結構和插入一行資料,下面其他的鎖也會同時用到這個表。
1 use test; 2 create table msq_test ( 3 id int primarykey, 4 status char(4)5 ) engine = innodb default character set = 'utf8'; 6 7 insert into msq_test(id, status) values(1, '1');
操作:1、set autocommit=0; 2、select .....for update實現鎖
注意:要使用悲觀鎖,我們必須關閉mysql資料庫的自動提交屬性,因為MySQL預設使用autocommit模式,也就是說,當你執行一個更新操作後,MySQL會立刻將結果進行提交。我們可以使用命令設定MySQL為非autocommit模式:set autocommit=0;
//開始事務 begin; //查詢出主鍵id=1的資訊 select status from t_goods where id=1 for update; //修改status為2 update msq_test set status=2; //提交事務 commit;
注:上面的begin/commit為事務的開始和結束,因為在前一步我們關閉了mysql的autocommit,所以需要手動控制事務的提交
在這裡,我們使用了select…for update的方式,這樣就通過資料庫實現了悲觀鎖。此時在msq_test表中,id為1的 那條資料就被我們鎖定了,其他事務的操作必須等待我們自己主動commit提交事務之後才能操作,這樣我們可以保證當前的資料不會被其它事務修改。
二、什麼是樂觀鎖 ?
1、樂觀鎖就是每次不加鎖而是假設沒有衝突而去完成某項操作,如果因為衝突失敗就重試,直到成功為止。
2、在mysql如何實現樂觀鎖?
樂觀鎖不是資料庫自帶的,需要我們自己去實現
1 use test; 2 create table msq_test ( 3 d int primary key, 4 status char(4), 5 version not null 6 ) engine = innodb default character set = 'utf8'; 7 8 insert into msq_test(id, status,version) values(1, '1',1);View Code
在上表中新增一個數字型別的 “version” 欄位來實現。當讀取資料時,將version欄位的值一同讀出,資料每更新一次,對此version值加一。當我們提交更新的時候,判斷資料庫表對應記錄的當前版本資訊與第一次取出來的version值進行比對,如果資料庫表當前版本號與第一次取出來的version值相等,則予以更新,否則認為是過期資料。
select * from msq_test where id =1;//得出一開始設定得version欄位(versionValue) update msq_test set status = newStatus,version = versionValue(老版本號) + 1 where version = versionValue;
這樣子就實現了樂觀鎖機制。
三、什麼是行鎖和表鎖?
行鎖:
行鎖,由字面意思理解,就是給某一行加上鎖,也就是一條記錄加上鎖。
SELECT * from msq_test where id = "1" lock in share mode;
在msq_test表中,id欄位為主鍵,就也相當於索引。執行加鎖時,會將id這個索引為1的記錄加上鎖,那麼這個鎖就是行鎖。
到這裡,我一開始也是很懵逼,啥是 lock in share mode???
select.....lock in share mode走的是IS鎖(意向共享鎖),即在符合條件的rows(行)上都加了共享鎖,這樣的話,其他session(事務)可以讀取這些記錄,也可以繼續新增IS鎖,但是無法修改這些記錄直到你這個加鎖的session執行完成(否則直接鎖等待超時)。
表鎖:
根據行鎖,你能理解啥是表鎖嗎?我想你智商比我高,應該可以。
不過我們需要注意一些鎖的級別,MySQL InnoDB預設Row-Level Lock,所以只有「明確」地指定主鍵,MySQL 才會執行Row lock (只鎖住被選取的資料) ,否則MySQL 將會執行Table Lock (將整個資料表單給鎖住)。
四、排他鎖
1、什麼是排他鎖
若事務T對資料物件A加上X鎖,則只允許T讀取和修改A,其他任何事務都不能再對A加任何型別的鎖,直到T釋放A上的鎖。這就保證了其他事務在T釋放A上的鎖之前不能再讀取和修改A。
啥?這定義太過複雜,難道買菜我會需要用高數去計算菜錢?我也覺得沒必要。所以可以認為上了排他鎖,其他執行緒既不能讀也不能修改。
2、如何用排他鎖
用法: select … for update;
例如:select * from msq_test where id = 1 for update;
排他鎖的申請前提:沒有執行緒對該結果集中的任何行資料使用排他鎖或共享鎖,否則申請會阻塞。
for update僅適用於InnoDB,且必須在事務塊(BEGIN/COMMIT)中才能生效。在進行事務操作時,通過“for update”語句,MySQL會對查詢結果集中每行資料都新增排他鎖,其他執行緒對該記錄的更新與刪除操作都會阻塞。排他鎖包含行鎖、表鎖。
注意:
事務保證整個操作的成一個組,要麼全做要麼全不做 但是不能保證多個事務同時讀取同一個資料 資料物件被加上排它鎖時,其他的事務不能對它讀取和修改;加了共享鎖的資料物件可以被其他事務讀取,但不能修改
文章的末尾,回到一開始的問題,為何要上鎖?鎖的具體實現上文已經給出。
答:為何上鎖?
事務可以用鎖實現,可以保證一致性和隔離性,但是鎖用來保證併發性;但是隔離性只是保證不會出現相互讀取中間資料(卻無法解決併發的問題)
為啥保持一致性的原因: MySQL會出現丟失更新:一個事務的更新覆蓋了其它事務的更新結果,就是所謂的更新丟失。例如:使用者A把值從11改為8,使用者B把值從8改為11,則使用者A丟失了他的更新。 為啥保持隔離性的原因:MySQL會出現髒讀:當一個事務讀取其它完成一半事務的記錄時,就會發生髒讀取。例如:使用者A,B看到的值都是9,使用者B把值改為5,使用者A讀到的值仍為9。