1. 程式人生 > >悲觀鎖,樂觀鎖,排他鎖,行鎖----MYSQL

悲觀鎖,樂觀鎖,排他鎖,行鎖----MYSQL

在說具體的鎖結構時,先思考一個問題,那就是為什麼要上鎖?然後我要如何選擇鎖?鎖具體如何實現?

在文章得末尾我給出了我的個人答案。

一、什麼是悲觀鎖?

  1、悲觀鎖就是在操作資料時,認為此操作會出現資料衝突,所以在進行每次操作時都要通過獲取鎖才能進行對相同資料的操作,這點跟java中的synchronized很相似。

  2、在MySQL中如何實現悲觀鎖。?

  mysql中有悲觀鎖的實現,我們想實現悲觀鎖時呼叫相對應得語句。

  測試用表的結構和插入一行資料,下面其他的鎖也會同時用到這個表。

1 use test;
2 create table msq_test (
3   id int primary
key, 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。