数据库高级知识——MySql锁机制
生活随笔
收集整理的這篇文章主要介紹了
数据库高级知识——MySql锁机制
小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.
文章目錄
- 1.概述
- 1.1 定義
- 1.2 鎖的分類
- 2.三鎖
- 2.1 表鎖(偏讀)
- 2.2 行鎖(偏寫)
- 2.3 頁鎖
1.概述
1.1 定義
鎖是計算機協(xié)調(diào)多個進程或線程并發(fā)訪問某一資源的機制。在數(shù)據(jù)庫中,除傳統(tǒng)的計算資源(如CPU、RAM、I/O等)的爭用以外,數(shù)據(jù)也是一種供許多用戶共享的資源。如何保證數(shù)據(jù)并發(fā)訪問的一致性、有效性是所有數(shù)據(jù)庫必須解決的一個問題,鎖沖突也是影響數(shù)據(jù)庫并發(fā)訪問性能的一個重要因素。從這個角度來說,鎖對數(shù)據(jù)庫而言顯得尤其重要,也更加復(fù)雜。1.2 鎖的分類
從對數(shù)據(jù)操作的類型(讀\寫)分:
讀鎖(共享鎖):針對同一份數(shù)據(jù),多個讀操作可以同時進行而不會互相影響。
寫鎖(排它鎖):當(dāng)前寫操作沒有完成前,它會阻斷其他寫鎖和讀鎖。
從對數(shù)據(jù)操作的粒度分:
為了盡可能提高數(shù)據(jù)庫的并發(fā)度,每次鎖定的數(shù)據(jù)范圍越小越好, 理論上每次只鎖定當(dāng)前操作的數(shù)據(jù)的方案會得到最大的并發(fā)度, 但是管理鎖是很耗資源的事情(涉及獲取,檢查,釋放鎖等動作), 因此數(shù)據(jù)庫系統(tǒng)需要在高并發(fā)響應(yīng)和系統(tǒng)性能兩方面進行平衡, 這樣就產(chǎn)生了“鎖粒度(Lock granularity)”的概念。一種提高共享資源并發(fā)發(fā)性的方式是讓鎖定對象更有選擇性。 盡量只鎖定需要修改的部分數(shù)據(jù),而不是所有的資源。 更理想的方式是,只對會修改的數(shù)據(jù)片進行精確的鎖定。 任何時候,在給定的資源上,鎖定的數(shù)據(jù)量越少,則系統(tǒng)的并發(fā)程度越高,只要相互之間不發(fā)生沖突即可。表鎖
行鎖
2.三鎖
2.1 表鎖(偏讀)
1.特點
偏向MyISAM存儲引擎,開銷小,加鎖快;無死鎖;鎖定粒度大,發(fā)生鎖沖突的概率最高,并發(fā)度最低。2.案例分析
【表級鎖分析--建表SQL】create table mylock(id int not null primary key auto_increment,name varchar(20) )engine myisam;insert into mylock(name) values('a'); insert into mylock(name) values('b'); insert into mylock(name) values('c'); insert into mylock(name) values('d'); insert into mylock(name) values('e');select * from mylock;【手動增加表鎖】lock table 表名字1 read(write),表名字2 read(write),其它; 【查看表上加過的鎖】 show open tables;【釋放表鎖】 unlock tables;3.結(jié)論
MyISAM在執(zhí)行查詢語句(SELECT)前,會自動給涉及的所有表加讀鎖, 在執(zhí)行增刪改操作前,會自動給涉及的表加寫鎖。 MySQL的表級鎖有兩種模式:表共享讀鎖(Table Read Lock)表獨占寫鎖(Table Write Lock)| 讀鎖 | 是 | 否 |
| 寫鎖 | 否 | 否 |
2.2 行鎖(偏寫)
1.特點
偏向InnoDB存儲引擎,開銷大,加鎖慢; 會出現(xiàn)死鎖; 鎖定粒度最小,發(fā)生鎖沖突的概率最低,并發(fā)度也最高。 InnoDB與MyISAM的最大不同有兩點:一是支持事務(wù)(TRANSACTION);二是采用了行級鎖2.由于行鎖支持事務(wù),復(fù)習(xí)老知識
事務(wù)筆記
事務(wù)(Transaction)及其ACID屬性
事務(wù)是由一組SQL語句組成的邏輯處理單元,事務(wù)具有以下4個屬性,通常簡稱為事務(wù)的ACID屬性。 l 原子性(Atomicity): 事務(wù)是一個原子操作單元,其對數(shù)據(jù)的修改,要么全都執(zhí)行,要么全都不執(zhí)行。 l 一致性(Consistent): 在事務(wù)開始和完成時,數(shù)據(jù)都必須保持一致狀態(tài)。 這意味著所有相關(guān)的數(shù)據(jù)規(guī)則都必須應(yīng)用于事務(wù)的修改,以保持數(shù)據(jù)的完整性; 事務(wù)結(jié)束時,所有的內(nèi)部數(shù)據(jù)結(jié)構(gòu)(如B樹索引或雙向鏈表)也都必須是正確的。 l 隔離性(Isolation): 數(shù)據(jù)庫系統(tǒng)提供一定的隔離機制, 保證事務(wù)在不受外部并發(fā)操作影響的“獨立”環(huán)境執(zhí)行。 這意味著事務(wù)處理過程中的中間狀態(tài)對外部是不可見的,反之亦然。l 持久性(Durable): 事務(wù)完成之后,它對于數(shù)據(jù)的修改是永久性的,即使出現(xiàn)系統(tǒng)故障也能夠保持。并發(fā)事務(wù)處理帶來的問題
更新丟失(Lost Update)
當(dāng)兩個或多個事務(wù)選擇同一行,然后基于最初選定的值更新該行時, 由于每個事務(wù)都不知道其他事務(wù)的存在,就會發(fā)生丟失更新問題--最后的更新覆蓋了由其他事務(wù)所做的更新。例如,兩個程序員修改同一java文件。每程序員獨立地更改其副本,然后保存更改后的副本, 這樣就覆蓋了原始文檔。最后保存其更改副本的編輯人員覆蓋前一個程序員所做的更改。如果在一個程序員完成并提交事務(wù)之前,另一個程序員不能訪問同一文件,則可避免此問題。臟讀(Dirty Reads)
一個事務(wù)正在對一條記錄做修改,在這個事務(wù)完成并提交前,這條記錄的數(shù)據(jù)就處于不一致狀態(tài); 這時,另一個事務(wù)也來讀取同一條記錄,如果不加控制, 第二個事務(wù)讀取了這些“臟”數(shù)據(jù),并據(jù)此做進一步的處理,就會產(chǎn)生未提交的數(shù)據(jù)依賴關(guān)系。 這種現(xiàn)象被形象地叫做”臟讀”。 一句話:事務(wù)A讀取到了事務(wù)B已修改但尚未提交的的數(shù)據(jù),還在這個數(shù)據(jù)基礎(chǔ)上做了操作。 此時,如果B事務(wù)回滾,A讀取的數(shù)據(jù)無效,不符合一致性要求。不可重復(fù)讀(Non-Repeatable Reads)
在一個事務(wù)內(nèi),多次讀同一個數(shù)據(jù)。 在這個事務(wù)還沒有結(jié)束時,另一個事務(wù)也訪問該同一數(shù)據(jù)。 那么,在第一個事務(wù)的兩次讀數(shù)據(jù)之間。 由于第二個事務(wù)的修改,那么第一個事務(wù)讀到的數(shù)據(jù)可能不一樣, 這樣就發(fā)生了在一個事務(wù)內(nèi)兩次讀到的數(shù)據(jù)是不一樣的,因此稱為不可重復(fù)讀,即原始讀取不可重復(fù)。一句話:一個事務(wù)范圍內(nèi)兩個相同的查詢卻返回了不同數(shù)據(jù)。幻讀(Phantom Reads)
一個事務(wù)按相同的查詢條件重新讀取以前檢索過的數(shù)據(jù),卻發(fā)現(xiàn)其他事務(wù)插入了滿足其查詢條件的新數(shù)據(jù),這種現(xiàn)象就稱為“幻讀”。一句話:事務(wù)A 讀取到了事務(wù)B提交的新增數(shù)據(jù),不符合隔離性。事務(wù)隔離級別
臟讀”、“不可重復(fù)讀”和“幻讀”,其實都是數(shù)據(jù)庫讀一致性問題,必須由數(shù)據(jù)庫提供一定的事務(wù)隔離機制來解決。 數(shù)據(jù)庫的事務(wù)隔離越嚴(yán)格,并發(fā)副作用越小,但付出的代價也就越大, 因為事務(wù)隔離實質(zhì)上就是使事務(wù)在一定程度上 “串行化”進行,這顯然與“并發(fā)”是矛盾的。 同時,不同的應(yīng)用對讀一致性和事務(wù)隔離程度的要求也是不同的, 比如許多應(yīng)用對“不可重復(fù)讀”和“幻讀”并不敏感,可能更關(guān)心數(shù)據(jù)并發(fā)訪問的能力。 常看當(dāng)前數(shù)據(jù)庫的事務(wù)隔離級別:show variables like 'transaction_isolation'; mysql> show variables like 'transaction_isolation'; +-----------------------+-----------------+ | Variable_name | Value | +-----------------------+-----------------+ | transaction_isolation | REPEATABLE-READ | +-----------------------+-----------------+ 1 row in set (0.01 sec)3.案例分析
建表SQL
create table test_innodb_lock (a int(11),b varchar(16))engine=innodb;insert into test_innodb_lock values(1,'b2'); insert into test_innodb_lock values(3,'3'); insert into test_innodb_lock values(4,'4000'); insert into test_innodb_lock values(5,'5000'); insert into test_innodb_lock values(6,'6000'); insert into test_innodb_lock values(7,'7000'); insert into test_innodb_lock values(8,'8000'); insert into test_innodb_lock values(9,'9000'); insert into test_innodb_lock values(1,'b1');create index test_innodb_a_ind on test_innodb_lock(a);create index test_innodb_lock_b_ind on test_innodb_lock(b);select * from test_innodb_lock;行鎖定基本演示
查看時候自動提交:
mysql> show variables like 'autocommit'; +---------------+-------+ | Variable_name | Value | +---------------+-------+ | autocommit | ON | +---------------+-------+ 1 row in set (0.01 sec)窗口1:
mysql> set autocommit=0; Query OK, 0 rows affected (0.03 sec)窗口2:
mysql> set autocommit=0; Query OK, 0 rows affected (0.03 sec)窗口1:
mysql> select * from test_innodb_lock; +------+------+ | a | b | +------+------+ | 1 | b2 | | 3 | 3 | | 4 | 4000 | | 5 | 5000 | | 6 | 6000 | | 7 | 7000 | | 8 | 8000 | | 9 | 9000 | | 1 | b1 | +------+------+ 9 rows in set (0.00 sec)mysql> update test_innodb_lock set b="4001" where a=4; Query OK, 1 row affected (0.02 sec) Rows matched: 1 Changed: 1 Warnings: 0mysql> select * from test_innodb_lock; +------+------+ | a | b | +------+------+ | 1 | b2 | | 3 | 3 | | 4 | 4001 | | 5 | 5000 | | 6 | 6000 | | 7 | 7000 | | 8 | 8000 | | 9 | 9000 | | 1 | b1 | +------+------+ 9 rows in set (0.00 sec)窗口2:
mysql> select * from test_innodb_lock; +------+------+ | a | b | +------+------+ | 1 | b2 | | 3 | 3 | | 4 | 4000 | | 5 | 5000 | | 6 | 6000 | | 7 | 7000 | | 8 | 8000 | | 9 | 9000 | | 1 | b1 | +------+------+ 9 rows in set (0.00 sec)窗口1:
mysql> commit; Query OK, 0 rows affected (0.07 sec)窗口2:
mysql> select * from test_innodb_lock; +------+------+ | a | b | +------+------+ | 1 | b2 | | 3 | 3 | | 4 | 4000 | | 5 | 5000 | | 6 | 6000 | | 7 | 7000 | | 8 | 8000 | | 9 | 9000 | | 1 | b1 | +------+------+ 9 rows in set (0.00 sec)mysql> commit; Query OK, 0 rows affected (0.00 sec)mysql> select * from test_innodb_lock; +------+------+ | a | b | +------+------+ | 1 | b2 | | 3 | 3 | | 4 | 4001 | | 5 | 5000 | | 6 | 6000 | | 7 | 7000 | | 8 | 8000 | | 9 | 9000 | | 1 | b1 | +------+------+ 9 rows in set (0.00 sec)無索引行鎖升級為表鎖
Select也可以加鎖
讀鎖:
共享鎖(Share Lock)共享鎖又稱讀鎖,是讀取操作創(chuàng)建的鎖。其他用戶可以并發(fā)讀取數(shù)據(jù), 但任何事務(wù)都不能對數(shù)據(jù)進行修改(獲取數(shù)據(jù)上的排他鎖),直到已釋放所有共享鎖。 如果事務(wù)T對數(shù)據(jù)A加上共享鎖后,則其他事務(wù)只能對A再加共享鎖,不能加排他鎖。 獲準(zhǔn)共享鎖的事務(wù)只能讀數(shù)據(jù),不能修改數(shù)據(jù)。用法 SELECT ... LOCK IN SHARE MODE;在查詢語句后面增加 LOCK IN SHARE MODE , Mysql會對查詢結(jié)果中的每行都加共享鎖, 當(dāng)沒有其他線程對查詢結(jié)果集中的任何一行使用排他鎖時,可以成功申請共享鎖,否則會被阻塞。 其他線程也可以讀取使用了共享鎖的表(行?),而且這些線程讀取的是同一個版本的數(shù)據(jù)。寫鎖:
排他鎖(eXclusive Lock)共享鎖又稱寫鎖,如果事務(wù)T對數(shù)據(jù)A加上排他鎖后,則其他事務(wù)不能再對A加任任何類型的封鎖。 獲準(zhǔn)排他鎖的事務(wù)既能讀數(shù)據(jù),又能修改數(shù)據(jù)。用法 SELECT ... FOR UPDATE;在查詢語句后面增加 FOR UPDATE ,Mysql會對查詢結(jié)果中的每行都加排他鎖, 當(dāng)沒有其他線程對查詢結(jié)果集中的任何一行使用排他鎖時,可以成功申請排他鎖,否則會被阻塞。間隙鎖危害
【什么是間隙鎖】 當(dāng)我們用范圍條件而不是相等條件檢索數(shù)據(jù),并請求共享或排他鎖時, InnoDB會給符合條件的已有數(shù)據(jù)記錄的索引項加鎖; 對于鍵值在條件范圍內(nèi)但并不存在的記錄,叫做“間隙(GAP)”, InnoDB也會對這個“間隙”加鎖,這種鎖機制就是所謂的間隙鎖(GAP Lock)。【危害】 因為Query執(zhí)行過程中通過過范圍查找的話,他會鎖定整個范圍內(nèi)所有的索引鍵值,即使這個鍵值并不存在。 間隙鎖有一個比較致命的弱點,就是當(dāng)鎖定一個范圍鍵值之后,即使某些不存在的鍵值也會被無辜的鎖定, 而造成在鎖定的時候無法插入鎖定鍵值范圍內(nèi)的任何數(shù)據(jù)。在某些場景下這可能會對性能造成很大的危害4.案列結(jié)論
Innodb存儲引擎由于實現(xiàn)了行級鎖定, 雖然在鎖定機制的實現(xiàn)方面所帶來的性能損耗可能比表級鎖定會要更高一些, 但是在整體并發(fā)處理能力方面要遠遠優(yōu)于MyISAM的表級鎖定的。 當(dāng)系統(tǒng)并發(fā)量較高的時候,Innodb的整體性能和MyISAM相比就會有比較明顯的優(yōu)勢了。但是,Innodb的行級鎖定同樣也有其脆弱的一面,當(dāng)我們使用不當(dāng)?shù)臅r候,可能會讓Innodb的整體性能表現(xiàn)不僅不能比MyISAM高,甚至可能會更差。5.行鎖分析
【如何分析行鎖定】 通過檢查InnoDB_row_lock狀態(tài)變量來分析系統(tǒng)上的行鎖的爭奪情況 mysql> show status like 'innodb_row_lock%'; +-------------------------------+-------+ | Variable_name | Value | +-------------------------------+-------+ | Innodb_row_lock_current_waits | 0 | | Innodb_row_lock_time | 0 | | Innodb_row_lock_time_avg | 0 | | Innodb_row_lock_time_max | 0 | | Innodb_row_lock_waits | 0 | +-------------------------------+-------+ 5 rows in set (0.00 sec)對各個狀態(tài)量的說明如下:Innodb_row_lock_current_waits:當(dāng)前正在等待鎖定的數(shù)量; Innodb_row_lock_time:從系統(tǒng)啟動到現(xiàn)在鎖定總時間長度; Innodb_row_lock_time_avg:每次等待所花平均時間; Innodb_row_lock_time_max:從系統(tǒng)啟動到現(xiàn)在等待最常的一次所花的時間; Innodb_row_lock_waits:系統(tǒng)啟動后到現(xiàn)在總共等待的次數(shù);對于這5個狀態(tài)變量,比較重要的主要是Innodb_row_lock_time_avg(等待平均時長),Innodb_row_lock_waits(等待總次數(shù))Innodb_row_lock_time(等待總時長)這三項。 尤其是當(dāng)?shù)却螖?shù)很高,而且每次等待時長也不小的時候, 我們就需要分析系統(tǒng)中為什么會有如此多的等待,然后根據(jù)分析結(jié)果著手指定優(yōu)化計劃。最后可以通過 SELECT * FROM information_schema.INNODB_TRX\G; 來查詢正在被鎖阻塞的sql語句。6.優(yōu)化建議
盡可能讓所有數(shù)據(jù)檢索都通過索引來完成,避免無索引行鎖升級為表鎖。 盡可能較少檢索條件,避免間隙鎖 盡量控制事務(wù)大小,減少鎖定資源量和時間長度 鎖住某行后,盡量不要去調(diào)別的行或表,趕緊處理被鎖住的行然后釋放掉鎖。 涉及相同表的事務(wù),對于調(diào)用表的順序盡量保持一致。 在業(yè)務(wù)環(huán)境允許的情況下,盡可能低級別事務(wù)隔離2.3 頁鎖
開銷和加鎖時間界于表鎖和行鎖之間;會出現(xiàn)死鎖;鎖定粒度界于表鎖和行鎖之間,并發(fā)度一般。總結(jié)
以上是生活随笔為你收集整理的数据库高级知识——MySql锁机制的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: C++类重点难点
- 下一篇: 数据库基础知识——视图