最美情侣中文字幕电影,在线麻豆精品传媒,在线网站高清黄,久久黄色视频

歡迎光臨散文網(wǎng) 會員登陸 & 注冊

淺析MySQL的鎖機(jī)制

2019-01-14 14:25 作者:動力節(jié)點(diǎn)  | 我要投稿

數(shù)據(jù)庫鎖定機(jī)制簡單來說就是數(shù)據(jù)庫為了保證數(shù)據(jù)的一致性而使各種共享資源在被并發(fā)訪問訪問變得有序所設(shè)計的一種規(guī)則;對于任何一種數(shù)據(jù)庫來說都需要有相應(yīng)的鎖定機(jī)制,Mysql也不例外。

?

Mysql幾種鎖定機(jī)制類型

?

MySQL 各存儲引擎使用了三種類型(級別)的鎖定機(jī)制:行級鎖定,頁級鎖定和表級鎖定。

?

1.行級鎖定

?

鎖定對象的顆粒度很小,只對當(dāng)前行進(jìn)行鎖定,所以發(fā)生鎖定資源爭用的概率也最小,能夠給予應(yīng)用程序盡可能大的并發(fā)處理能力;弊端就是獲取鎖釋放鎖更加頻繁,系統(tǒng)消耗更大,同時行級鎖定也最容易發(fā)生死鎖;
行級鎖定的主要是Innodb存儲引擎和NDB Cluster存儲引擎;

?

2.頁級鎖定

?

鎖定顆粒度介于行級鎖定與表級鎖之間,每頁有多行數(shù)據(jù),并發(fā)處理能力以及獲取鎖定所需要的資源開銷在兩者之間;
頁級鎖定主要是BerkeleyDB 存儲引擎;

?

3.表級鎖定

?

一次會將整張表鎖定,該鎖定機(jī)制最大的特點(diǎn)是實(shí)現(xiàn)邏輯非常簡單,帶來的系統(tǒng)負(fù)面影響最小,而且可以避免死鎖問題;弊端就是鎖定資源爭用的概率最高,并發(fā)處理能力最低;
使用表級鎖定的主要是MyISAM,Memory,CSV等一些非事務(wù)性存儲引擎。

本文重點(diǎn)介紹Innodb存儲引擎使用的行級鎖定;

?

兩段鎖協(xié)議(2PL)

兩段鎖協(xié)議規(guī)定所有的事務(wù)應(yīng)遵守的規(guī)則:
1.在對任何數(shù)據(jù)進(jìn)行讀、寫操作之前,首先要申請并獲得對該數(shù)據(jù)的封鎖;
2.在釋放一個封鎖之后,事務(wù)不再申請和獲得其它任何封鎖;

?

即事務(wù)的執(zhí)行分為兩個階段:

第一階段是獲得封鎖的階段,稱為擴(kuò)展階段;第二階段是釋放封鎖的階段,稱為收縮階段;

begin;
insert ... ? 加鎖1
update ... ? 加鎖2
commit; ? ? ?事務(wù)提交時,釋放鎖1,鎖2

如果在加鎖2的時候,加鎖不成功,則進(jìn)入等待狀態(tài),直到加鎖成功才繼續(xù)執(zhí)行;
如果有另外一個事務(wù)獲取鎖的時候順序剛好相反,是有可能導(dǎo)致死鎖的;為此有了一次性封鎖法,要求事務(wù)必須一次性將所有要使用的數(shù)據(jù)全部加鎖,否則就不能繼續(xù)執(zhí)行;

定理:若所有事務(wù)均遵守兩段鎖協(xié)議,則這些事務(wù)的所有交叉調(diào)度都是可串行化的(串行化很重要,尤其是在數(shù)據(jù)恢復(fù)和備份的時候);

?

行級鎖定(悲觀鎖)

?

1.共享鎖和排他鎖

Innodb的行級鎖定同樣分為兩種類型:共享鎖和排他鎖;
共享鎖:當(dāng)一個事務(wù)獲得共享鎖之后,它只可以進(jìn)行讀操作,所以共享鎖也叫讀鎖,多個事務(wù)可以同時獲得某一行數(shù)據(jù)的共享鎖;
排他鎖:而當(dāng)一個事務(wù)獲得一行數(shù)據(jù)的排他鎖時,就可以對該行數(shù)據(jù)進(jìn)行讀和寫操作,所以排他鎖也叫寫鎖,排他鎖與共享鎖和其他的排他鎖不兼容;

?

既然數(shù)據(jù)庫提供了共享鎖和排他鎖,那具體用在什么地方:
1.1在數(shù)據(jù)庫操作中,為了有效保證并發(fā)讀取數(shù)據(jù)的正確性,提出的事務(wù)隔離級別,隔離級別就使用了鎖機(jī)制;
1.2提供了相關(guān)的SQL,可以方便的在程序中使用;

?

2.事務(wù)隔離級別和鎖的關(guān)系

數(shù)據(jù)庫隔離級別:未提交讀(Read uncommitted),已提交讀(Read committed),可重復(fù)讀(Repeatable read)和可串行化(Serializable);
未提交讀(Read uncommitted):可能讀取到其他會話中未提交事務(wù)修改的數(shù)據(jù),會出現(xiàn)臟讀(Dirty Read);
已提交讀(Read committed):只能讀取到已經(jīng)提交的數(shù)據(jù),會出現(xiàn)不可重復(fù)讀(NonRepeatable Read);
可重復(fù)讀(Repeatable read):InnoDB默認(rèn)級別,不會出現(xiàn)不可重復(fù)讀(NonRepeatable Read),但是會出現(xiàn)幻讀(Phantom Read);
可串行化(Serializable):強(qiáng)制事務(wù)排序,使之不可能相互沖突,從而解決幻讀問題,使用表級共享鎖,讀寫相互都會阻塞;

?

常用的2種隔離級別是:已提交讀(Read committed)和可重復(fù)讀(Repeatable read);

?

3.已提交讀

3.1準(zhǔn)備測試表
CREATE TABLE `test_lock` (
? `id` int(11) NOT NULL AUTO_INCREMENT,
? `name` varchar(255) NOT NULL,
? `type` int(11) DEFAULT NULL,
? PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8
?
mysql> insert into test_lock values(null,'zhaohui',1);
mysql> insert into test_lock values(null,'zhaohui2',2);
3.2查看和設(shè)置隔離級別
mysql> SELECT @@tx_isolation;
+-----------------+
| @@tx_isolation ?|
+-----------------+
| REPEATABLE-READ |
+-----------------+
1 row in set
?
mysql> set session transaction isolation level read committed;
Query OK, 0 rows affected
?
mysql> SELECT @@tx_isolation;
+----------------+
| @@tx_isolation |
+----------------+
| READ-COMMITTED |
+----------------+
3.3模擬多個事務(wù)交叉執(zhí)行
Session1執(zhí)行查詢

mysql> begin;
Query OK, 0 rows affected
mysql> select * from test_lock where id=1;
+----+---------+------+
| id | name ? ?| type |
+----+---------+------+
| ?1 | zhaohui | ? ?1 |
+----+---------+------+
1 row in set
Session2更新數(shù)據(jù)

mysql> begin;
Query OK, 0 rows affected
?
mysql> update test_lock set name='zhaohui_new' where id=1;
??
Query OK, 1 row affected
Rows matched: 1 ?Changed: 1 ?Warnings: 0
mysql> commit;
Query OK, 0 rows affected
Session1執(zhí)行查詢

mysql> select * from test_lock where id=1;
+----+-------------+------+
| id | name ? ? ? ?| type |
+----+-------------+------+
| ?1 | zhaohui_new | ? ?1 |
+----+-------------+------+
1 row in set
?
mysql> commit;
Query OK, 0 rows affected
Session1中出現(xiàn)了不可重復(fù)讀(NonRepeatable Read),也就是在查詢的時候沒有鎖住相關(guān)的數(shù)據(jù),導(dǎo)致出現(xiàn)了不可重復(fù)讀,但是寫入、修改和刪除數(shù)據(jù)還是加鎖了,如下所示:

Session1更新數(shù)據(jù)

mysql> begin;
Query OK, 0 rows affected
?
mysql> update test_lock set name='zhaohui_new2' where id=1;
Query OK, 1 row affected
Rows matched: 1 ?Changed: 1 ?Warnings: 0
Session2更新數(shù)據(jù)

mysql> begin;
Query OK, 0 rows affected
?
mysql> update test_lock set name='zhaohui_new3' where id=1;
1205 - Lock wait timeout exceeded; try restarting transaction
Session2更新在更新同一條數(shù)據(jù)的時候超時了,在更新數(shù)據(jù)的時候添加了排他鎖;

?

4.可重復(fù)讀

4.1查看和設(shè)置隔離級別
mysql> set session transaction isolation level repeatable read;
Query OK, 0 rows affected
?
mysql> SELECT @@tx_isolation;
+-----------------+
| @@tx_isolation ?|
+-----------------+
| REPEATABLE-READ |
+-----------------+
1 row in set
4.2模擬多個事務(wù)交叉執(zhí)行
Session1執(zhí)行查詢

mysql> begin;
Query OK, 0 rows affected
?
mysql> select * from test_lock where type=2;
+----+----------+------+
| id | name ? ? | type |
+----+----------+------+
| ?2 | zhaohui2 | ? ?2 |
+----+----------+------+
1 row in set
Session2更新數(shù)據(jù)

mysql> begin;
Query OK, 0 rows affected
?
mysql> update test_lock set name='zhaohui2_new' where type=2;
Query OK, 1 row affected
Rows matched: 1 ?Changed: 1 ?Warnings: 0
?
mysql> commit;
Query OK, 0 rows affected
Session1執(zhí)行查詢

mysql> select * from test_lock where type=2;
+----+----------+------+
| id | name ? ? | type |
+----+----------+------+
| ?2 | zhaohui2 | ? ?2 |
+----+----------+------+
1 row in set
可以發(fā)現(xiàn)2次查詢的數(shù)據(jù)結(jié)果是一樣的,實(shí)現(xiàn)了可重復(fù)讀(Repeatable read),再來看一下是否有幻讀(Phantom Read)的問題;

Session3插入數(shù)據(jù)

mysql> begin;
Query OK, 0 rows affected
?
mysql> insert into test_lock values(null,'zhaohui3',2);
Query OK, 1 row affected
?
mysql> commit;
Query OK, 0 rows affected
Session1執(zhí)行查詢

mysql> select * from test_lock where type=2;
+----+----------+------+
| id | name ? ? | type |
+----+----------+------+
| ?2 | zhaohui2 | ? ?2 |
+----+----------+------+
1 row in set
可以發(fā)現(xiàn)可重復(fù)讀(Repeatable read)隔離級別下,也不會出現(xiàn)幻讀的現(xiàn)象;

分析一下原因:如何通過悲觀鎖的方式去實(shí)現(xiàn)可重復(fù)讀和不出現(xiàn)幻讀的現(xiàn)象,對讀取的數(shù)據(jù)加共享鎖,對同樣的數(shù)據(jù)執(zhí)行更新操作就只能等待,這樣就可以保證可重復(fù)讀,但是對于不出現(xiàn)幻讀的現(xiàn)象無法通過鎖定行數(shù)據(jù)來解決;
最終看到的現(xiàn)象是沒有幻讀的問題,同時如果對讀取的數(shù)據(jù)加共享鎖,更新相同數(shù)據(jù)應(yīng)該會等待,上面的實(shí)例中并沒有出現(xiàn)等待,所以mysql內(nèi)部應(yīng)該還有其他鎖機(jī)制--MVCC機(jī)制;

?

5.悲觀鎖SQL使用

5.1共享鎖使用(lock in share mode)
Session1查詢數(shù)據(jù)

mysql> begin;
Query OK, 0 rows affected
?
mysql> select * from test_lock where type=2 lock in share mode;
+----+--------------+------+
| id | name ? ? ? ? | type |
+----+--------------+------+
| ?2 | zhaohui2_new | ? ?2 |
| ?3 | zhaohui3 ? ? | ? ?2 |
+----+--------------+------+
2 rows in set
Session2查詢數(shù)據(jù)

mysql> begin;
Query OK, 0 rows affected
?
mysql> select * from test_lock where type=2 lock in share mode;
+----+--------------+------+
| id | name ? ? ? ? | type |
+----+--------------+------+
| ?2 | zhaohui2_new | ? ?2 |
| ?3 | zhaohui3 ? ? | ? ?2 |
+----+--------------+------+
2 rows in set
Session3更新數(shù)據(jù)

mysql> begin;
Query OK, 0 rows affected
?
mysql> update test_lock set name='zhaohui3_new' where id=3;
1205 - Lock wait timeout exceeded; try restarting transaction
Session1和Session2使用了共享鎖,所以可以存在多個,并不沖突,但是Session3更新操作需要加上排他鎖,和共享鎖不能同時存在;

5.2排他鎖使用(for update)
Session1查詢數(shù)據(jù)

mysql> begin;
Query OK, 0 rows affected
?
mysql> select * from test_lock where type=2 for update;
+----+--------------+------+
| id | name ? ? ? ? | type |
+----+--------------+------+
| ?2 | zhaohui2_new | ? ?2 |
| ?3 | zhaohui3 ? ? | ? ?2 |
+----+--------------+------+
2 rows in set
Session2查詢數(shù)據(jù)

mysql> begin;
Query OK, 0 rows affected
?
mysql> select * from test_lock where type=2 for update;
Empty set
Session3更新數(shù)據(jù)

mysql> begin;
Query OK, 0 rows affected
?
mysql> update test_lock set name='zhaohui3_new' where id=3;
1205 - Lock wait timeout exceeded; try restarting transaction
排他鎖只能有一個同時存在,所有Session2和Session3都將等等超時;

?

?

多版本并發(fā)控制MVCC

?

多版本并發(fā)控制(Multiversion Concurrency Control):每一個寫操作都會創(chuàng)建一個新版本的數(shù)據(jù),讀操作會從有限多個版本的數(shù)據(jù)中挑選一個最合適的結(jié)果直接返回;讀寫操作之間的沖突就不再需要被關(guān)注,而管理和快速挑選數(shù)據(jù)的版本就成了MVCC需要解決的主要問題。
為什么要引入此機(jī)制,首先通過悲觀鎖來處理讀請求是很耗性能的,其次數(shù)據(jù)庫的事務(wù)大都是只讀的,讀請求是寫請求的很多倍,最后如果沒有并發(fā)控制機(jī)制,最壞的情況也是讀請求讀到了已經(jīng)寫入的數(shù)據(jù),這對很多應(yīng)用完全是可以接受的;

?

再來看一下可重復(fù)讀(Repeatable read)現(xiàn)象,通過MVCC機(jī)制讀操作只讀該事務(wù)開始前的數(shù)據(jù)庫的快照(snapshot), 這樣在讀操作不用阻塞寫操作,寫操作不用阻塞讀操作的同時,避免了臟讀和不可重復(fù)讀;

?

當(dāng)然并不是說悲觀鎖就沒有用了,在數(shù)據(jù)更新的時候數(shù)據(jù)庫默認(rèn)還是使用悲觀鎖的,所以MVCC是可以整合起來一起使用的(MVCC+2PL),用來解決讀-寫沖突的無鎖并發(fā)控制;
MVCC使用快照讀的方式,解決了不可重復(fù)讀和幻讀的問題,如上面的實(shí)例所示:select查詢的一直是快照信息,不需要添加任何鎖;
以上實(shí)例中使用的select方式把它稱為快照讀(snapshot read),其實(shí)事務(wù)的隔離級別的讀還有另一層含義:讀取數(shù)據(jù)庫當(dāng)前版本數(shù)據(jù)–當(dāng)前讀(current read);

?

當(dāng)前讀和Gap鎖

區(qū)別普通的select查詢,當(dāng)前讀對應(yīng)的sql包括:

select ...for update,
select ...lock in share mode,
insert,update,delete;
以上sql本身會加悲觀鎖,所以不存在不可重復(fù)讀的問題,剩下的就是幻讀的問題;
Session1執(zhí)行當(dāng)前讀

mysql> select * from test_lock where type=2 for update;
+----+----------------+------+
| id | name ? ? ? ? ? | type |
+----+----------------+------+
| ?2 | zhaohui2_new ? | ? ?2 |
| ?3 | zhaohui3_new_1 | ? ?2 |
+----+----------------+------+
2 rows in set
Session2執(zhí)行插入

mysql> begin;
Query OK, 0 rows affected
?
mysql> insert into test_lock values(null,'zhaohui_001',1);
1205 - Lock wait timeout exceeded; try restarting transaction
為什么明明鎖住的是type=2的數(shù)據(jù),當(dāng)插入type=1也會鎖等待,因?yàn)镮nnoDB對于行的查詢都是采用了Next-Key鎖,鎖定的不是單個值,而是一個范圍(GAP);
如果當(dāng)前type類型包括:1,2,4,6,8,10鎖住type=2,那么type=1,2,3會被鎖住,后面的不會,鎖住的是一個區(qū)間;這樣也就保證了當(dāng)前讀也不會出現(xiàn)幻讀的現(xiàn)象;
注:type字段添加了索引,如果沒有添加索引,gap鎖會鎖住整張表;

?

樂觀鎖

?

樂觀鎖是一種思想,認(rèn)為事務(wù)間爭用沒有那么多,和悲觀鎖是相對的,樂觀鎖在java的并發(fā)包中大量的使用;一般采用以下方式:使用版本號(version)機(jī)制來實(shí)現(xiàn),版本號就是為數(shù)據(jù)添加一個版本標(biāo)志,一般在表中添加一個version字段;當(dāng)讀取數(shù)據(jù)的時候把version也取出來,然后version+1,更新數(shù)據(jù)庫的時候?qū)Ρ鹊谝淮稳〕鰜淼膙ersion和數(shù)據(jù)庫里面的version是否一致,如果一致則更新成功,否則失敗進(jìn)入重試,具體使用大致如下:

begin;
select id,name,version from test_lock where id=1;
....
update test_lock set name='xxx',version=version+1 where id=1 and version=${version};
commit;
先查詢后更新,需要保證原子性,要么使用悲觀鎖的方式,對整個事務(wù)加鎖;要么使用樂觀鎖的方式,如果在讀多寫少的系統(tǒng)中,樂觀鎖性能更好;

?

總結(jié)

本文首先從Mysql的悲觀鎖出發(fā),然后介紹了悲觀鎖和事務(wù)隔離級別之間的關(guān)系,并分析為什么沒有使用悲觀鎖來實(shí)現(xiàn)隔離級別;然后從問題出發(fā)分別介紹了MVCC和Gap鎖是如何解決了不可重復(fù)讀的問題和幻讀的問題;最后介紹了樂觀鎖經(jīng)常被用在讀數(shù)據(jù)遠(yuǎn)大于寫數(shù)據(jù)的系統(tǒng)中。

淺析MySQL的鎖機(jī)制的評論 (共 條)

分享到微博請遵守國家法律
登封市| 永济市| 宁阳县| 南丹县| 遂溪县| 治县。| 信丰县| 鞍山市| 兴城市| 龙里县| 万年县| 湛江市| 霞浦县| 子洲县| 张家口市| 和平区| 桐庐县| 尼玛县| 赣州市| 南开区| 胶州市| 鄢陵县| 桐庐县| 北川| 罗甸县| 精河县| 乌拉特中旗| 彰化县| 西乌珠穆沁旗| 项城市| 阿克| 嘉峪关市| 尉犁县| 宝坻区| 天气| 唐山市| 兰坪| 南昌县| 邛崃市| 冷水江市| 报价|