詳細解析mysql鎖機制

2022-03-16 19:00:53
本篇文章給大家帶來了關於的相關知識,其中主要介紹了mysql中各種鎖的機制問題,鎖是資料庫為了保證資料的一致性,而使用各種共用的資源在被並行存取時變得有序所設計的一種規則,希望對大家有幫助。

推薦學習:

Mysql鎖:

在多執行緒當中如果想保證資料的準確性是如何實現的呢?沒錯,通過同步實現。同步就相當於是加鎖。加了鎖以後有什麼好處呢?當一個執行緒真正在運算元據的時候,其他執行緒只能等待。當一個執行緒執行完畢後,釋放鎖。其他執行緒才能進行操作!

那麼我們的MySQL資料庫中的鎖的功能也是類似的,處理事務的隔離性中,可能會出現髒讀、不可重複讀、幻讀的問題,所以,鎖的作用也可以解決這些問題!

在資料庫中,資料是一種供許多使用者共用存取的資源,如何保證資料並行存取的一致性、有效性,是所有資料庫必須解決的一個問題,MySQL由於自身架構的特點,在不同的儲存引擎中,都設計了面對特定場景的鎖定機制,所以引擎的差別,導致鎖機制也是有很大差別的。

鎖機制 :

資料庫為了保證資料的一致性,而使用各種共用的資源在被並行存取時變得有序所設計的一種規則。

舉例:在電商網站購買商品時,商品表中只存有1個商品,而此時又有兩個人同時購買,那麼誰能買到就是一個關鍵的問題。

這裡會用到事務進行一系列的操作:

先從商品表中取出物品的資料

然後插入訂單

付款後,再插入付款表資訊

更新商品表中商品的數量

以上過程中,使用鎖可以對商品數量資料資訊進行保護,實現隔離,即只允許第一位使用者完成整套購買流程,而其他使用者只能等待,這樣就解決了並行中的矛盾問題。

鎖的分類:

按操作分類:

共用鎖:也叫讀鎖。針對同一份資料,多個事務讀取操作可以同時加鎖而不互相影響 ,但是不能修改資料記錄。

排他鎖:也叫寫鎖。當前的操作沒有完成前,會阻斷其他操作的讀取和寫入

按粒度分類:

表級鎖:操作時,會鎖定整個表。開銷小,加鎖快;不會出現死鎖;鎖定力度大,發生鎖衝突概率高,並行度最低。偏向於MyISAM儲存引擎!

行級鎖:操作時,會鎖定當前操作行。開銷大,加鎖慢;會出現死鎖;鎖定粒度小,發生鎖衝突的概率低,並行度高。偏向於InnoDB儲存引擎!

頁級鎖:鎖的粒度、發生衝突的概率和加鎖的開銷介於表鎖和行鎖之間,會出現死鎖,並行效能一般。

按使用方式分類:

悲觀鎖:每次查詢資料時都認為別人會修改,很悲觀,所以查詢時加鎖。

樂觀鎖:每次查詢資料時都認為別人不會修改,很樂觀,但是更新時會判斷一下在此期間別人有沒有去更新這個資料

不同儲存引擎支援的鎖

63.png

共用鎖:

多個共用鎖之間可以共用,如果是有鍵的話InnoDB預設是行鎖,沒有的話就會提升到表鎖,是行鎖時多個視窗可以修改不同行的資料,同行的話需要等先加鎖的提交,不同行可以直接修改,但是另外一個要查詢也要等後面修改的提交。提交完鎖就消失了

共用鎖:

SELECT語句 LOCK IN SHARE MODE;

視窗1:

- 視窗1
/*
    共用鎖:資料可以被多個事務查詢,但是不能修改
*/
-- 開啟事務
START TRANSACTION;
-- 查詢id為1的資料記錄。加入共用鎖
SELECT * FROM student WHERE id=1 LOCK IN SHARE MODE;
-- 查詢分數為99分的資料記錄。加入共用鎖
SELECT * FROM student WHERE score=99 LOCK IN SHARE MODE;
-- 提交事務
COMMIT;

視窗2:

-- 視窗2
-- 開啟事務
START TRANSACTION;
-- 查詢id為1的資料記錄(普通查詢,可以查詢)
SELECT * FROM student WHERE id=1;
-- 查詢id為1的資料記錄,並加入共用鎖(可以查詢。共用鎖和共用鎖相容)
SELECT * FROM student WHERE id=1 LOCK IN SHARE MODE;
-- 修改id為1的姓名為張三三(不能修改,會出現鎖的情況。只有視窗1提交事務後,才能修改成功)
UPDATE student SET NAME='張三三' WHERE id = 1;
-- 修改id為2的姓名為李四四(修改成功,InnoDB引擎預設是行鎖)
UPDATE student SET NAME='李四四' WHERE id = 2;
-- 修改id為3的姓名為王五五(修改失敗,InnoDB引擎如果不採用帶索引的列加鎖。則會提升為表鎖)
UPDATE student SET NAME='王五五' WHERE id = 3;
-- 提交事務
COMMIT;

排他鎖:

在排他鎖執行的時候,其他事務普通查詢可以,不可以加鎖任何操作

-- 標準語法
SELECT語句 FOR UPDATE;

視窗1:

-- 視窗1
/*
    排他鎖:加鎖的資料,不能被其他事務加鎖查詢或修改
*/
-- 開啟事務
START TRANSACTION;
-- 查詢id為1的資料記錄,並加入排他鎖
SELECT * FROM student WHERE id=1 FOR UPDATE;
-- 提交事務
COMMIT;

視窗2:

-- 視窗2
-- 開啟事務
START TRANSACTION;
-- 查詢id為1的資料記錄(普通查詢沒問題)
SELECT * FROM student WHERE id=1;
-- 查詢id為1的資料記錄,並加入共用鎖(不能查詢。因為排他鎖不能和其他鎖共存)
SELECT * FROM student WHERE id=1 LOCK IN SHARE MODE;
-- 查詢id為1的資料記錄,並加入排他鎖(不能查詢。因為排他鎖不能和其他鎖共存)
SELECT * FROM student WHERE id=1 FOR UPDATE;
-- 修改id為1的姓名為張三(不能修改,會出現鎖的情況。只有視窗1提交事務後,才能修改成功)
UPDATE student SET NAME='張三' WHERE id=1;
-- 提交事務
COMMIT;

MyISAM鎖:

MyISAM讀鎖:

myisam是加整個表的鎖,讀鎖的時候,不解鎖的話所有的事務可以查,不可以有其他任何操作包括本身事務也不可以操作

-- 加鎖
LOCK TABLE 表名 READ;
-- 解鎖(將當前對談所有的表進行解鎖)
UNLOCK TABLES;

MyISAM寫鎖:

寫鎖的時候,只要不解鎖其他事務不可以執行任何操作,本身事務可以操作

-- 標準語法
-- 加鎖
LOCK TABLE 表名 WRITE;
-- 解鎖(將當前對談所有的表進行解鎖)
UNLOCK TABLES;

悲觀鎖:

就是很悲觀,它對於資料被外界修改的操作持保守態度,認為資料隨時會修改。

整個資料處理中需要將資料加鎖。悲觀鎖一般都是依靠關係型資料庫提供的鎖機制。

行鎖,表鎖不論是讀寫鎖都是悲觀鎖。

樂觀鎖:

就是很樂觀,每次自己運算元據的時候認為沒有人會來修改它,所以不去加鎖。

但是在更新的時候會去判斷在此期間資料有沒有被修改。

需要使用者自己去實現,不會發生並行搶佔資源,只有在提交操作的時候檢查是否違反資料完整性。

樂觀鎖的簡單實現方式:

實現思想:加標記去比較,一樣則執行,不同則不執行

方式一:版本號

給資料表中新增一個version列,每次更新後都將這個列的值加1。

讀取資料時,將版本號讀取出來,在執行更新的時候,比較版本號。

如果相同則執行更新,如果不相同,說明此條資料已經發生了變化。

使用者自行根據這個通知來決定怎麼處理,比如重新開始一遍,或者放棄本次更新。

-- 建立city表
CREATE TABLE city(
    id INT PRIMARY KEY AUTO_INCREMENT,  -- 城市id
    NAME VARCHAR(20),                   -- 城市名稱
    VERSION INT                         -- 版本號
);
-- 新增資料
INSERT INTO city VALUES (NULL,'北京',1),(NULL,'上海',1),(NULL,'廣州',1),(NULL,'深圳',1);
-- 修改北京為北京市
-- 1.查詢北京的version
SELECT VERSION FROM city WHERE NAME='北京';
-- 2.修改北京為北京市,版本號+1。並對比版本號
UPDATE city SET NAME='北京市',VERSION=VERSION+1 WHERE NAME='北京' AND VERSION=1;

方式二:時間戳

和版本號方式基本一樣,給資料表中新增一個列,名稱無所謂,資料型別需要是timestamp

每次更新後都將最新時間插入到此列。

讀取資料時,將時間讀取出來,在執行更新的時候,比較時間。

如果相同則執行更新,如果不相同,說明此條資料已經發生了變化。

悲觀鎖和樂觀鎖使用前提:

對於讀的操作遠多於寫的操作的時候,這時候一個更新操作加鎖會阻塞所有的讀取操作,降低了吞吐量。最後還要釋放鎖,鎖是需要一些開銷的,這時候可以選擇樂觀鎖。

如果是讀寫比例差距不是非常大或者系統沒有響應不及時,吞吐量瓶頸的問題,那就不要去使用樂觀鎖,它增加了複雜度,也帶來了業務額外的風險。這時候可以選擇悲觀鎖。

推薦學習:

以上就是詳細解析mysql鎖機制的詳細內容,更多請關注TW511.COM其它相關文章!