MySQL的鎖機(jī)制

MySQL鎖簡介

MySQL的鎖機(jī)制比較簡單,其最顯著的特點是不同的存儲引擎支持不同的鎖機(jī)制。比如泽篮,MyISAM和MEMORY存儲引擎采用的是表級鎖(table-level locking);InnoDB存儲引擎既支持行級鎖(row-level locking)唐断,也支持表級鎖,但默認(rèn)情況下是采用行級鎖杭抠。

表級鎖:開銷小脸甘,加鎖快;不會出現(xiàn)死鎖偏灿;鎖定粒度大丹诀,發(fā)生鎖沖突的概率最高,并發(fā)度最低翁垂。
行級鎖:開銷大铆遭,加鎖慢;會出現(xiàn)死鎖沿猜;鎖定粒度最小枚荣,發(fā)生鎖沖突的概率最低,并發(fā)度也最高啼肩。

從上述特點可見橄妆,很難籠統(tǒng)地說哪種鎖更好,只能就具體應(yīng)用的特點來說哪種鎖更合適祈坠!僅從鎖的角度 來說:表級鎖更適合于以查詢?yōu)橹骱δ耄挥猩倭堪此饕龡l件更新數(shù)據(jù)的應(yīng)用,如Web應(yīng)用赦拘;而行級鎖則更適合于有大量按索引條件并發(fā)更新少量不同數(shù)據(jù)慌随,同時又有 并發(fā)查詢的應(yīng)用,如一些在線事務(wù)處理(OLTP)系統(tǒng)躺同。

前提準(zhǔn)備:

  1. 提前建一個測試用表
CREATE TABLE `lock_table` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `NAME` varchar(20) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;

CREATE TABLE `user` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `NAME` varchar(20) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;

INSERT INTO `lock_table` (`id`, `NAME`) VALUES ('1', 'a');
INSERT INTO `lock_table` (`id`, `NAME`) VALUES ('2', 'b');
INSERT INTO `lock_table` (`id`, `NAME`) VALUES ('3', 'c');
INSERT INTO `lock_table` (`id`, `NAME`) VALUES ('4', 'd');

INSERT INTO `user` (`id`, `NAME`) VALUES ('1', 'zhangsan');

2.開啟兩個mysql回話儒陨,并關(guān)閉autocommit

set @@autocommit=OFF;

MyISAM表鎖

MySQL的表級鎖有兩種模式:表共享讀鎖(Table Read Lock)表獨占寫鎖(Table Write Lock)

對MyISAM表的讀操作笋籽,不會阻塞其他用戶對同一表的讀請求蹦漠,但會阻塞對同一表的寫請求;對 MyISAM表的寫操作车海,則會阻塞其他用戶對同一表的讀和寫操作笛园;MyISAM表的讀操作與寫操作之間,以及寫操作之間是串行的侍芝!

寫鎖阻塞讀的案例

會話1 會話2
獲取表寫鎖
lock table lock_table write;
當(dāng)前會話對表增刪改查都可以執(zhí)行
select * from lock_table;
insert into lock_table values(5,'e');
delete from lock_table where id=5;
當(dāng)前會話對表的查詢操作會被阻塞
select * from lock_table;
釋放鎖
unlock tables;
會話1釋放鎖之后當(dāng)前會話能夠立即執(zhí)行研铆,并查詢出結(jié)果

讀鎖阻塞寫的案例

會話1 會話2
獲取表讀鎖
lock table lock_table read;
當(dāng)前會話可以查詢數(shù)據(jù)
select * from lock_table;
當(dāng)前會話可以查詢數(shù)據(jù)
select * from lock_table;
當(dāng)前會話不能查詢沒有鎖定的表
select * from user;
ERROR 1100 (HY000): Table 'user' was not locked with LOCK TABLES
當(dāng)前會話可以查詢或者更新未鎖定的表
select * from lock_table;
select * from user;
insert into user (id, NAME) VALUES ('4', 'wangwu');
當(dāng)前會話進(jìn)行插入或更新表會提示錯誤
update lock_table set name='aa' where id =1;
ERROR 1099 (HY000): Table 'lock_table' was locked with a READ lock and can't be updated
insert into lock_table values(9,'i');
ERROR 1099 (HY000): Table 'lock_table' was locked with a READ lock and can't be updated
當(dāng)前會話可讀取
select * from lock_table;
當(dāng)前會話進(jìn)行寫操作會阻塞等待獲得鎖
update lock_table set name='aa' where id =1;
釋放鎖
unlock tables;
獲得鎖,更新成功

總結(jié)

MyISAM在執(zhí)行查詢語句之前州叠,會自動給涉及的所有表加讀鎖棵红,在執(zhí)行更新操作前,會自動給涉及的表加寫鎖咧栗,這個過程并不需要用戶干預(yù)逆甜,因此用戶一般不需要使用命令來顯式加鎖虱肄,上例中的加鎖時為了演示效果。

InnoDB鎖

1. 事務(wù)

事務(wù)是由一組SQL語句組成的邏輯處理單元交煞,事務(wù)具有4屬性咏窿,通常稱為事務(wù)的ACID屬性。

原子性(Actomicity):事務(wù)是一個原子操作單元素征,其對數(shù)據(jù)的修改集嵌,要么全都執(zhí)行,要么全都不執(zhí)行御毅。
一致性(Consistent):在事務(wù)開始和完成時根欧,數(shù)據(jù)都必須保持一致狀態(tài)。
隔離性(Isolation):數(shù)據(jù)庫系統(tǒng)提供一定的隔離機(jī)制端蛆,保證事務(wù)在不受外部并發(fā)操作影響的“獨立”環(huán)境執(zhí)行凤粗。
持久性(Durable):事務(wù)完成之后,它對于數(shù)據(jù)的修改是永久性的欺税,即使出現(xiàn)系統(tǒng)故障也能夠保持侈沪。

2.并發(fā)情況下事務(wù)帶來的問題

相對于串行處理來說揭璃,并發(fā)事務(wù)處理能大大增加數(shù)據(jù)庫資源的利用率晚凿,提高數(shù)據(jù)庫系統(tǒng)的事務(wù)吞吐量,從而可以支持更多用戶的并發(fā)操作瘦馍,但與此同時歼秽,會帶來一下問題:

臟讀: 一個事務(wù)在更新一條記錄,未提交前情组,第二個事務(wù)讀到了第一個事務(wù)更新后的記錄燥筷,那么第二個事務(wù)就讀到了臟數(shù)據(jù),會產(chǎn)生對第一個未提交數(shù)據(jù)的依賴院崇。一旦第一個事務(wù)回滾肆氓,那么第二個事務(wù)讀到的數(shù)據(jù),將是錯誤的臟數(shù)據(jù)底瓣。

不可重復(fù)讀:一個事務(wù)在讀取某些數(shù)據(jù)后的一段時間后谢揪,再次讀取這個數(shù)據(jù),發(fā)現(xiàn)其讀取出來的數(shù)據(jù)內(nèi)容已經(jīng)發(fā)生了改變捐凭,就是不可重復(fù)讀拨扶。

幻讀: 一個事務(wù)按相同的查詢條件查詢之前檢索過的數(shù)據(jù),確發(fā)現(xiàn)檢索出來的結(jié)果集條數(shù)變多或者減少(由其他事務(wù)插入茁肠、刪除的)患民,類似產(chǎn)生幻覺。

上述出現(xiàn)的問題都是數(shù)據(jù)庫讀一致性的問題垦梆,可以通過事務(wù)的隔離機(jī)制來進(jìn)行保證匹颤。

隔離級別 臟讀 幻讀 不可重復(fù)讀
Read Uncommitted(讀取未提交內(nèi)容)
Read Committed(讀取提交內(nèi)容)
Repeatable Read(可重讀)
Serializable(可串行化)

3.InnoDB行鎖及加鎖方法

共享鎖(S):允許一個事務(wù)去讀一行仅孩,阻止其他事務(wù)獲得相同的數(shù)據(jù)集的排他鎖。
排他鎖(X):允許獲得排他鎖的事務(wù)更新數(shù)據(jù)惋嚎,但是組織其他事務(wù)獲得相同數(shù)據(jù)集的共享鎖和排他鎖杠氢。
意向共享鎖(IS):表示事務(wù)準(zhǔn)備給數(shù)據(jù)行加入共享鎖,也就是說一個數(shù)據(jù)行加共享鎖前必須先取得該表的IS鎖
意向排他鎖(IX):類似上面另伍,表示事務(wù)準(zhǔn)備給數(shù)據(jù)行加入排他鎖鼻百,說明事務(wù)在一個數(shù)據(jù)行加排他鎖前必須先取得該表的IX鎖。

MySQL InnoDB引擎默認(rèn)的修改數(shù)據(jù)語句:update,delete,insert都會自動給涉及到的數(shù)據(jù)加上排他鎖摆尝,select語句默認(rèn)不會加任何鎖類型温艇,如果加排他鎖可以使用select …for update語句,加共享鎖可以使用select … lock in share mode語句堕汞。所以加過排他鎖的數(shù)據(jù)行在其他事務(wù)種是不能修改數(shù)據(jù)的勺爱,也不能通過for update和lock in share mode鎖的方式查詢數(shù)據(jù),但可以直接通過select …from…查詢數(shù)據(jù)讯检,因為普通查詢沒有任何鎖機(jī)制琐鲁。

InnoDB行鎖實現(xiàn)方式

InnoDB行鎖是通過給索引上的索引項加鎖來實現(xiàn)的,這一點MySQL與Oracle不同人灼,后者是通過在數(shù)據(jù)塊中對相應(yīng)數(shù)據(jù)行加鎖來實現(xiàn)的围段。InnoDB這種行鎖實現(xiàn)特點意味著:只有通過索引條件檢索數(shù)據(jù),InnoDB才使用行級鎖投放,否則奈泪,InnoDB將使用表鎖!

1.在不通過索引查詢的時候灸芳,innodb使用的是表鎖而不是行鎖
創(chuàng)建表

CREATE TABLE `person_no_index` (
  `id` int(11) DEFAULT NULL,
  `name` varchar(30) DEFAULT NULL,
  `age` int(11) DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

insert into person_no_index (id,name,age) values (1,"張三",25);
insert into person_no_index (id,name,age) values (2,"李四",28);
insert into person_no_index (id,name,age) values (3,"趙六",59);
會話1 會話2
begin;
select * from person_no_index where id = 1 for update;
begin;
select * from person_no_index where id = 2 for update;
此時該會話會阻塞等待排它鎖釋放
commit; 會話1釋放排它鎖之后可查詢出數(shù)據(jù)

會話1只給一行加了排他鎖涝桅,但是會話2在請求其他行的排他鎖的時候,會出現(xiàn)鎖等待烙样。原因是在沒有索引的情況下冯遂,innodb只能使用表鎖。

2.通過索引查詢的時候谒获,innodb使用的是行鎖
創(chuàng)建表

CREATE TABLE `person_index` (
  `id` int(11) DEFAULT NULL,
  `name` varchar(30) DEFAULT NULL,
  `age` int(11) DEFAULT NULL,
  KEY `name_idx` (`name`),
  KEY `age_idx` (`age`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

insert into person_index (id,name,age) values (1,"張三",25);
insert into person_index (id,name,age) values (2,"李四",28);
insert into person_index (id,name,age) values (3,"趙六",59);
會話1 會話2
begin;
select * from person_index where name = '張三' for update;
begin;
select * from person_index where name = '李四' for update;(此時可以查詢出數(shù)據(jù))
select * from person_index where name = '張三' for update;(此時進(jìn)入阻塞狀態(tài)蛤肌,等待會話1釋放鎖)
commit; 會話1釋放排它鎖之后可查詢出name='張三'的數(shù)據(jù)

會話1只給 name=張三 的行加了排他鎖,會話2在請求其他行的排他鎖的時候究反,可以正常查詢寻定,在查詢 name=張三 時出現(xiàn)鎖阻塞,所以此時使用的是行鎖精耐。

2.由于MySQL的行鎖是針對索引加的鎖狼速,不是針對記錄加的鎖,所以雖然是訪問不同行的記錄卦停,但是如果是使用相同的索引鍵向胡,是會出現(xiàn)沖突的恼蓬。

插入一條name=張三 id不同的數(shù)據(jù)
insert into person_index (id,name,age) values (4,"張三",88);
此時表中數(shù)據(jù)為:
+------+--------+------+
| id   | name   | age  |
+------+--------+------+
|    1 | 張三   |   25 |
|    2 | 李四   |   28 |
|    3 | 趙六   |   59 |
|    4 | 張三   |   88 |
+------+--------+------+
會話1 會話2
begin;
select * from person_index where id=1 and name='張三' for update;
begin;
select * from person_index where id=4 and name='張三' for update;
name字段有索引,雖然查詢的數(shù)據(jù)是不同行記錄僵芹,但是因為索引鍵一樣处硬,會出現(xiàn)阻塞
commit; 會話1釋放鎖后可查詢出數(shù)據(jù)

總結(jié)

對于MyISAM的表鎖,主要討論了以下幾點:
(1)共享讀鎖(S)之間是兼容的拇派,但共享讀鎖(S)與排他寫鎖(X)之間荷辕,以及排他寫鎖(X)之間是互斥的,也就是說讀和寫是串行的件豌。
(2)在一定條件下疮方,MyISAM允許查詢和插入并發(fā)執(zhí)行,我們可以利用這一點來解決應(yīng)用中對同一表查詢和插入的鎖爭用問題茧彤。
(3)MyISAM默認(rèn)的鎖調(diào)度機(jī)制是寫優(yōu)先骡显,這并不一定適合所有應(yīng)用,用戶可以通過設(shè)置LOW_PRIORITY_UPDATES參數(shù)曾掂,或在INSERT惫谤、UPDATE、DELETE語句中指定LOW_PRIORITY選項來調(diào)節(jié)讀寫鎖的爭用珠洗。
(4)由于表鎖的鎖定粒度大溜歪,讀寫之間又是串行的,因此险污,如果更新操作較多痹愚,MyISAM表可能會出現(xiàn)嚴(yán)重的鎖等待富岳,可以考慮采用InnoDB表來減少鎖沖突蛔糯。

對于InnoDB表,本文主要討論了以下幾項內(nèi)容:
(1)InnoDB的行鎖是基于索引實現(xiàn)的窖式,如果不通過索引訪問數(shù)據(jù)蚁飒,InnoDB會使用表鎖。
(2)在不同的隔離級別下萝喘,InnoDB的鎖機(jī)制和一致性讀策略不同淮逻。

在了解InnoDB鎖特性后,用戶可以通過設(shè)計和SQL調(diào)整等措施減少鎖沖突和死鎖阁簸,包括:

  • 盡量使用較低的隔離級別爬早; 精心設(shè)計索引,并盡量使用索引訪問數(shù)據(jù)启妹,使加鎖更精確筛严,從而減少鎖沖突的機(jī)會;
  • 選擇合理的事務(wù)大小饶米,小事務(wù)發(fā)生鎖沖突的幾率也更薪翱小车胡;
  • 給記錄集顯式加鎖時,最好一次性請求足夠級別的鎖照瘾。比如要修改數(shù)據(jù)的話匈棘,最好直接申請排他鎖,而不是先申請共享鎖析命,修改時再請求排他鎖渐溶,這樣容易產(chǎn)生死鎖;
  • 不同的程序訪問一組表時讲竿,應(yīng)盡量約定以相同的順序訪問各表翅娶,對一個表而言,盡可能以固定的順序存取表中的行昼浦。這樣可以大大減少死鎖的機(jī)會馍资;
  • 盡量用相等條件訪問數(shù)據(jù),這樣可以避免間隙鎖對并發(fā)插入的影響关噪; 不要申請超過實際需要的鎖級別鸟蟹;除非必須,查詢時不要顯示加鎖使兔;
  • 對于一些特定的事務(wù)建钥,可以使用表鎖來提高處理速度或減少死鎖的可能。
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
禁止轉(zhuǎn)載虐沥,如需轉(zhuǎn)載請通過簡信或評論聯(lián)系作者熊经。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市欲险,隨后出現(xiàn)的幾起案子镐依,更是在濱河造成了極大的恐慌,老刑警劉巖天试,帶你破解...
    沈念sama閱讀 206,378評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件槐壳,死亡現(xiàn)場離奇詭異,居然都是意外死亡喜每,警方通過查閱死者的電腦和手機(jī)务唐,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,356評論 2 382
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來带兜,“玉大人枫笛,你說我怎么就攤上這事「照眨” “怎么了刑巧?”我有些...
    開封第一講書人閱讀 152,702評論 0 342
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我海诲,道長繁莹,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,259評論 1 279
  • 正文 為了忘掉前任特幔,我火速辦了婚禮咨演,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘蚯斯。我一直安慰自己薄风,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 64,263評論 5 371
  • 文/花漫 我一把揭開白布拍嵌。 她就那樣靜靜地躺著遭赂,像睡著了一般。 火紅的嫁衣襯著肌膚如雪横辆。 梳的紋絲不亂的頭發(fā)上撇他,一...
    開封第一講書人閱讀 49,036評論 1 285
  • 那天,我揣著相機(jī)與錄音狈蚤,去河邊找鬼困肩。 笑死,一個胖子當(dāng)著我的面吹牛脆侮,可吹牛的內(nèi)容都是我干的锌畸。 我是一名探鬼主播,決...
    沈念sama閱讀 38,349評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼靖避,長吁一口氣:“原來是場噩夢啊……” “哼潭枣!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起幻捏,我...
    開封第一講書人閱讀 36,979評論 0 259
  • 序言:老撾萬榮一對情侶失蹤盆犁,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后粘咖,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體蚣抗,經(jīng)...
    沈念sama閱讀 43,469評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡侈百,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,938評論 2 323
  • 正文 我和宋清朗相戀三年瓮下,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片钝域。...
    茶點故事閱讀 38,059評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡讽坏,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出例证,到底是詐尸還是另有隱情路呜,我是刑警寧澤,帶...
    沈念sama閱讀 33,703評論 4 323
  • 正文 年R本政府宣布,位于F島的核電站胀葱,受9級特大地震影響漠秋,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜抵屿,卻給世界環(huán)境...
    茶點故事閱讀 39,257評論 3 307
  • 文/蒙蒙 一庆锦、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧轧葛,春花似錦搂抒、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,262評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至衷笋,卻和暖如春芳杏,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背辟宗。 一陣腳步聲響...
    開封第一講書人閱讀 31,485評論 1 262
  • 我被黑心中介騙來泰國打工蚜锨, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人慢蜓。 一個月前我還...
    沈念sama閱讀 45,501評論 2 354
  • 正文 我出身青樓亚再,卻偏偏與公主長得像,于是被迫代替她去往敵國和親晨抡。 傳聞我的和親對象是個殘疾皇子氛悬,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 42,792評論 2 345

推薦閱讀更多精彩內(nèi)容

  • 引言 數(shù)據(jù)庫鎖定機(jī)制簡單來說就是數(shù)據(jù)庫為了保證數(shù)據(jù)的一致性而使各種共享資源在被并發(fā)訪問變得有序所設(shè)計的一種規(guī)則如捅;對...
    高級java架構(gòu)師閱讀 719評論 0 1
  • 1、什么是鎖调煎? 就是數(shù)據(jù)庫為了保證數(shù)據(jù)的一致性镜遣,而使各種共享資源在被并發(fā)訪問時變得有序所設(shè)計的一種規(guī)則 2、鎖...
    bug_ling閱讀 228評論 0 0
  • 鎖概述 MySQL的鎖機(jī)制士袄,就是數(shù)據(jù)庫為了保證數(shù)據(jù)的一致性而設(shè)計的面對并發(fā)場景的一種規(guī)則悲关。 最顯著的特點是不同的存...
    胡一巴閱讀 432評論 0 0
  • 我是黑夜里大雨紛飛的人啊 1 “又到一年六月,有人笑有人哭娄柳,有人歡樂有人憂愁寓辱,有人驚喜有人失落,有的覺得收獲滿滿有...
    陌忘宇閱讀 8,523評論 28 53
  • 信任包括信任自己和信任他人 很多時候赤拒,很多事情秫筏,失敗诱鞠、遺憾、錯過这敬,源于不自信航夺,不信任他人 覺得自己做不成,別人做不...
    吳氵晃閱讀 6,181評論 4 8