详细解析mysql锁机制(超详细Redis入门教程)
本篇文章給大家?guī)砹岁P(guān)于mysql的相關(guān)知識,其中主要介紹了mysql中各種鎖的機(jī)制問題,鎖是數(shù)據(jù)庫為了保證數(shù)據(jù)的一致性,而使用各種共享的資源在被并發(fā)訪問時(shí)變得有序所設(shè)計(jì)的一種規(guī)則,希望對大家有幫助。
推薦學(xué)習(xí):mysql教程
Mysql鎖:
在多線程當(dāng)中如果想保證數(shù)據(jù)的準(zhǔn)確性是如何實(shí)現(xiàn)的呢?沒錯(cuò),通過同步實(shí)現(xiàn)。同步就相當(dāng)于是加鎖。加了鎖以后有什么好處呢?當(dāng)一個(gè)線程真正在操作數(shù)據(jù)的時(shí)候,其他線程只能等待。當(dāng)一個(gè)線程執(zhí)行完畢后,釋放鎖。其他線程才能進(jìn)行操作!
那么我們的MySQL數(shù)據(jù)庫中的鎖的功能也是類似的,處理事務(wù)的隔離性中,可能會出現(xiàn)臟讀、不可重復(fù)讀、幻讀的問題,所以,鎖的作用也可以解決這些問題!
在數(shù)據(jù)庫中,數(shù)據(jù)是一種供許多用戶共享訪問的資源,如何保證數(shù)據(jù)并發(fā)訪問的一致性、有效性,是所有數(shù)據(jù)庫必須解決的一個(gè)問題,MySQL由于自身架構(gòu)的特點(diǎn),在不同的存儲引擎中,都設(shè)計(jì)了面對特定場景的鎖定機(jī)制,所以引擎的差別,導(dǎo)致鎖機(jī)制也是有很大差別的。
鎖機(jī)制 :
數(shù)據(jù)庫為了保證數(shù)據(jù)的一致性,而使用各種共享的資源在被并發(fā)訪問時(shí)變得有序所設(shè)計(jì)的一種規(guī)則。
舉例:在電商網(wǎng)站購買商品時(shí),商品表中只存有1個(gè)商品,而此時(shí)又有兩個(gè)人同時(shí)購買,那么誰能買到就是一個(gè)關(guān)鍵的問題。
這里會用到事務(wù)進(jìn)行一系列的操作:
先從商品表中取出物品的數(shù)據(jù)
然后插入訂單
付款后,再插入付款表信息
更新商品表中商品的數(shù)量
以上過程中,使用鎖可以對商品數(shù)量數(shù)據(jù)信息進(jìn)行保護(hù),實(shí)現(xiàn)隔離,即只允許第一位用戶完成整套購買流程,而其他用戶只能等待,這樣就解決了并發(fā)中的矛盾問題。
鎖的分類:
按操作分類:
共享鎖:也叫讀鎖。針對同一份數(shù)據(jù),多個(gè)事務(wù)讀取操作可以同時(shí)加鎖而不互相影響 ,但是不能修改數(shù)據(jù)記錄。
排他鎖:也叫寫鎖。當(dāng)前的操作沒有完成前,會阻斷其他操作的讀取和寫入
按粒度分類:
表級鎖:操作時(shí),會鎖定整個(gè)表。開銷小,加鎖快;不會出現(xiàn)死鎖;鎖定力度大,發(fā)生鎖沖突概率高,并發(fā)度最低。偏向于MyISAM存儲引擎!
行級鎖:操作時(shí),會鎖定當(dāng)前操作行。開銷大,加鎖慢;會出現(xiàn)死鎖;鎖定粒度小,發(fā)生鎖沖突的概率低,并發(fā)度高。偏向于InnoDB存儲引擎!
頁級鎖:鎖的粒度、發(fā)生沖突的概率和加鎖的開銷介于表鎖和行鎖之間,會出現(xiàn)死鎖,并發(fā)性能一般。
按使用方式分類:
悲觀鎖:每次查詢數(shù)據(jù)時(shí)都認(rèn)為別人會修改,很悲觀,所以查詢時(shí)加鎖。
樂觀鎖:每次查詢數(shù)據(jù)時(shí)都認(rèn)為別人不會修改,很樂觀,但是更新時(shí)會判斷一下在此期間別人有沒有去更新這個(gè)數(shù)據(jù)
不同存儲引擎支持的鎖
共享鎖:
多個(gè)共享鎖之間可以共享,如果是有鍵的話InnoDB默認(rèn)是行鎖,沒有的話就會提升到表鎖,是行鎖時(shí)多個(gè)窗口可以修改不同行的數(shù)據(jù),同行的話需要等先加鎖的提交,不同行可以直接修改,但是另外一個(gè)要查詢也要等后面修改的提交。提交完鎖就消失了
共享鎖:
SELECT語句 LOCK IN SHARE MODE;
登錄后復(fù)制
窗口1:
- 窗口1
/*
共享鎖:數(shù)據(jù)可以被多個(gè)事務(wù)查詢,但是不能修改
*/
-- 開啟事務(wù)
START TRANSACTION;
-- 查詢id為1的數(shù)據(jù)記錄。加入共享鎖
SELECT * FROM student WHERE id=1 LOCK IN SHARE MODE;
-- 查詢分?jǐn)?shù)為99分的數(shù)據(jù)記錄。加入共享鎖
SELECT * FROM student WHERE score=99 LOCK IN SHARE MODE;
-- 提交事務(wù)
COMMIT;
登錄后復(fù)制
窗口2:
-- 窗口2 -- 開啟事務(wù) START TRANSACTION; -- 查詢id為1的數(shù)據(jù)記錄(普通查詢,可以查詢) SELECT * FROM student WHERE id=1; -- 查詢id為1的數(shù)據(jù)記錄,并加入共享鎖(可以查詢。共享鎖和共享鎖兼容) SELECT * FROM student WHERE id=1 LOCK IN SHARE MODE; -- 修改id為1的姓名為張三三(不能修改,會出現(xiàn)鎖的情況。只有窗口1提交事務(wù)后,才能修改成功) UPDATE student SET NAME='張三三' WHERE id = 1; -- 修改id為2的姓名為李四四(修改成功,InnoDB引擎默認(rèn)是行鎖) UPDATE student SET NAME='李四四' WHERE id = 2; -- 修改id為3的姓名為王五五(修改失敗,InnoDB引擎如果不采用帶索引的列加鎖。則會提升為表鎖) UPDATE student SET NAME='王五五' WHERE id = 3; -- 提交事務(wù) COMMIT;
登錄后復(fù)制
排他鎖:
在排他鎖執(zhí)行的時(shí)候,其他事務(wù)普通查詢可以,不可以加鎖任何操作
-- 標(biāo)準(zhǔn)語法 SELECT語句 FOR UPDATE;
登錄后復(fù)制
窗口1:
-- 窗口1
/*
排他鎖:加鎖的數(shù)據(jù),不能被其他事務(wù)加鎖查詢或修改
*/
-- 開啟事務(wù)
START TRANSACTION;
-- 查詢id為1的數(shù)據(jù)記錄,并加入排他鎖
SELECT * FROM student WHERE id=1 FOR UPDATE;
-- 提交事務(wù)
COMMIT;
登錄后復(fù)制
窗口2:
-- 窗口2 -- 開啟事務(wù) START TRANSACTION; -- 查詢id為1的數(shù)據(jù)記錄(普通查詢沒問題) SELECT * FROM student WHERE id=1; -- 查詢id為1的數(shù)據(jù)記錄,并加入共享鎖(不能查詢。因?yàn)榕潘i不能和其他鎖共存) SELECT * FROM student WHERE id=1 LOCK IN SHARE MODE; -- 查詢id為1的數(shù)據(jù)記錄,并加入排他鎖(不能查詢。因?yàn)榕潘i不能和其他鎖共存) SELECT * FROM student WHERE id=1 FOR UPDATE; -- 修改id為1的姓名為張三(不能修改,會出現(xiàn)鎖的情況。只有窗口1提交事務(wù)后,才能修改成功) UPDATE student SET NAME='張三' WHERE id=1; -- 提交事務(wù) COMMIT;
登錄后復(fù)制
MyISAM鎖:
MyISAM讀鎖:
myisam是加整個(gè)表的鎖,讀鎖的時(shí)候,不解鎖的話所有的事務(wù)可以查,不可以有其他任何操作包括本身事務(wù)也不可以操作
-- 加鎖 LOCK TABLE 表名 READ; -- 解鎖(將當(dāng)前會話所有的表進(jìn)行解鎖) UNLOCK TABLES;
登錄后復(fù)制
MyISAM寫鎖:
寫鎖的時(shí)候,只要不解鎖其他事務(wù)不可以執(zhí)行任何操作,本身事務(wù)可以操作
-- 標(biāo)準(zhǔn)語法 -- 加鎖 LOCK TABLE 表名 WRITE; -- 解鎖(將當(dāng)前會話所有的表進(jìn)行解鎖) UNLOCK TABLES;
登錄后復(fù)制
悲觀鎖:
就是很悲觀,它對于數(shù)據(jù)被外界修改的操作持保守態(tài)度,認(rèn)為數(shù)據(jù)隨時(shí)會修改。
整個(gè)數(shù)據(jù)處理中需要將數(shù)據(jù)加鎖。悲觀鎖一般都是依靠關(guān)系型數(shù)據(jù)庫提供的鎖機(jī)制。
行鎖,表鎖不論是讀寫鎖都是悲觀鎖。
樂觀鎖:
就是很樂觀,每次自己操作數(shù)據(jù)的時(shí)候認(rèn)為沒有人會來修改它,所以不去加鎖。
但是在更新的時(shí)候會去判斷在此期間數(shù)據(jù)有沒有被修改。
需要用戶自己去實(shí)現(xiàn),不會發(fā)生并發(fā)搶占資源,只有在提交操作的時(shí)候檢查是否違反數(shù)據(jù)完整性。
樂觀鎖的簡單實(shí)現(xiàn)方式:
實(shí)現(xiàn)思想:加標(biāo)記去比較,一樣則執(zhí)行,不同則不執(zhí)行
方式一:版本號
給數(shù)據(jù)表中添加一個(gè)version列,每次更新后都將這個(gè)列的值加1。
讀取數(shù)據(jù)時(shí),將版本號讀取出來,在執(zhí)行更新的時(shí)候,比較版本號。
如果相同則執(zhí)行更新,如果不相同,說明此條數(shù)據(jù)已經(jīng)發(fā)生了變化。
用戶自行根據(jù)這個(gè)通知來決定怎么處理,比如重新開始一遍,或者放棄本次更新。
-- 創(chuàng)建city表
CREATE TABLE city(
id INT PRIMARY KEY AUTO_INCREMENT, -- 城市id
NAME VARCHAR(20), -- 城市名稱
VERSION INT -- 版本號
);
-- 添加數(shù)據(jù)
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;
登錄后復(fù)制
方式二:時(shí)間戳
和版本號方式基本一樣,給數(shù)據(jù)表中添加一個(gè)列,名稱無所謂,數(shù)據(jù)類型需要是timestamp
每次更新后都將最新時(shí)間插入到此列。
讀取數(shù)據(jù)時(shí),將時(shí)間讀取出來,在執(zhí)行更新的時(shí)候,比較時(shí)間。
如果相同則執(zhí)行更新,如果不相同,說明此條數(shù)據(jù)已經(jīng)發(fā)生了變化。
悲觀鎖和樂觀鎖使用前提:
對于讀的操作遠(yuǎn)多于寫的操作的時(shí)候,這時(shí)候一個(gè)更新操作加鎖會阻塞所有的讀取操作,降低了吞吐量。最后還要釋放鎖,鎖是需要一些開銷的,這時(shí)候可以選擇樂觀鎖。
如果是讀寫比例差距不是非常大或者系統(tǒng)沒有響應(yīng)不及時(shí),吞吐量瓶頸的問題,那就不要去使用樂觀鎖,它增加了復(fù)雜度,也帶來了業(yè)務(wù)額外的風(fēng)險(xiǎn)。這時(shí)候可以選擇悲觀鎖。
推薦學(xué)習(xí):mysql學(xué)習(xí)教程
以上就是詳細(xì)解析mysql鎖機(jī)制的詳細(xì)內(nèi)容,更多請關(guān)注風(fēng)君子博客其它相關(guān)文章!
總結(jié)
以上是生活随笔為你收集整理的详细解析mysql锁机制(超详细Redis入门教程)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Hybris Commerce下单时遇到
- 下一篇: 一个能极大提高生产率的Chrome新建标