推薦學習:
在多執行緒當中如果想保證資料的準確性是如何實現的呢?沒錯,通過同步實現。同步就相當於是加鎖。加了鎖以後有什麼好處呢?當一個執行緒真正在運算元據的時候,其他執行緒只能等待。當一個執行緒執行完畢後,釋放鎖。其他執行緒才能進行操作!
那麼我們的MySQL資料庫中的鎖的功能也是類似的,處理事務的隔離性中,可能會出現髒讀、不可重複讀、幻讀的問題,所以,鎖的作用也可以解決這些問題!
在資料庫中,資料是一種供許多使用者共用存取的資源,如何保證資料並行存取的一致性、有效性,是所有資料庫必須解決的一個問題,MySQL由於自身架構的特點,在不同的儲存引擎中,都設計了面對特定場景的鎖定機制,所以引擎的差別,導致鎖機制也是有很大差別的。
資料庫為了保證資料的一致性,而使用各種共用的資源在被並行存取時變得有序所設計的一種規則。
舉例:在電商網站購買商品時,商品表中只存有1個商品,而此時又有兩個人同時購買,那麼誰能買到就是一個關鍵的問題。
這裡會用到事務進行一系列的操作:
先從商品表中取出物品的資料
然後插入訂單
付款後,再插入付款表資訊
更新商品表中商品的數量
以上過程中,使用鎖可以對商品數量資料資訊進行保護,實現隔離,即只允許第一位使用者完成整套購買流程,而其他使用者只能等待,這樣就解決了並行中的矛盾問題。
按操作分類:
共用鎖:也叫讀鎖。針對同一份資料,多個事務讀取操作可以同時加鎖而不互相影響 ,但是不能修改資料記錄。
排他鎖:也叫寫鎖。當前的操作沒有完成前,會阻斷其他操作的讀取和寫入
按粒度分類:
表級鎖:操作時,會鎖定整個表。開銷小,加鎖快;不會出現死鎖;鎖定力度大,發生鎖衝突概率高,並行度最低。偏向於MyISAM儲存引擎!
行級鎖:操作時,會鎖定當前操作行。開銷大,加鎖慢;會出現死鎖;鎖定粒度小,發生鎖衝突的概率低,並行度高。偏向於InnoDB儲存引擎!
頁級鎖:鎖的粒度、發生衝突的概率和加鎖的開銷介於表鎖和行鎖之間,會出現死鎖,並行效能一般。
按使用方式分類:
悲觀鎖:每次查詢資料時都認為別人會修改,很悲觀,所以查詢時加鎖。
樂觀鎖:每次查詢資料時都認為別人不會修改,很樂觀,但是更新時會判斷一下在此期間別人有沒有去更新這個資料
不同儲存引擎支援的鎖
多個共用鎖之間可以共用,如果是有鍵的話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是加整個表的鎖,讀鎖的時候,不解鎖的話所有的事務可以查,不可以有其他任何操作包括本身事務也不可以操作
-- 加鎖 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其它相關文章!