1勿决、概述
鎖的定義
1.鎖是計(jì)算機(jī)協(xié)調(diào)多個(gè)進(jìn)程或線程并發(fā)訪問某一資源的機(jī)制。
2.在數(shù)據(jù)庫中近速,除傳統(tǒng)的計(jì)算資源(如CPU、RAM堪旧、I/O等)的爭(zhēng)用以外数焊,數(shù)據(jù)也是一種供許多用戶共享的資源。
3.如何保證數(shù)據(jù)并發(fā)訪問的一致性崎场、有效性是所有數(shù)據(jù)庫必須解決的一個(gè)問題佩耳,鎖沖突也是影響數(shù)據(jù)庫并發(fā)訪問性能的一個(gè)重要因素。
4.從這個(gè)角度來說谭跨,鎖對(duì)數(shù)據(jù)庫而言顯得尤其重要干厚,也更加復(fù)雜。
鎖的分類
1.從數(shù)據(jù)操作的類型(讀螃宙、寫)分
- 讀鎖(共享鎖):針對(duì)同一份數(shù)據(jù)蛮瞄,多個(gè)讀操作可以同時(shí)進(jìn)行而不會(huì)互相影響
- 寫鎖(排它鎖):當(dāng)前寫操作沒有完成前,它會(huì)阻斷其他寫鎖和讀鎖谆扎。
2.從對(duì)數(shù)據(jù)操作的顆粒度
- 表鎖
- 行鎖
2挂捅、表鎖
表鎖的特點(diǎn)
偏向MyISAM存儲(chǔ)引擎,開銷小堂湖,加鎖快闲先,無死鎖,鎖定粒度大无蜂,發(fā)生鎖沖突的概率最高伺糠,并發(fā)最低
2.1表鎖案例分析
創(chuàng)建表
- 建表 SQL:引擎選擇 myisam
create table mylock (
id int not null primary key auto_increment,
name varchar(20) default ''
) 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;
- mylock 表中的測(cè)試數(shù)據(jù)
mysql> select * from mylock;
+----+------+
| id | name |
+----+------+
| 1 | a |
| 2 | b |
| 3 | c |
| 4 | d |
| 5 | e |
+----+------+
5 rows in set (0.00 sec)
手動(dòng)加鎖和釋放鎖
- 查看當(dāng)前數(shù)據(jù)庫中表的上鎖情況:show open tables;,0 表示未上鎖
mysql> show open tables;
+--------------------+----------------------------------------------------+--------+-------------+
| Database | Table | In_use | Name_locked |
+--------------------+----------------------------------------------------+--------+-------------+
| performance_schema | events_waits_history | 0 | 0 |
| performance_schema | events_waits_summary_global_by_event_name | 0 | 0 |
| performance_schema | setup_timers | 0 | 0 |
| performance_schema | events_waits_history_long | 0 | 0 |
| performance_schema | events_statements_summary_by_digest | 0 | 0 |
| performance_schema | mutex_instances | 0 | 0 |
| performance_schema | events_waits_summary_by_instance | 0 | 0 |
| performance_schema | events_stages_history | 0 | 0 |
| mysql | db | 0 | 0 |
| performance_schema | events_waits_summary_by_host_by_event_name | 0 | 0 |
| mysql | user | 0 | 0 |
| mysql | columns_priv | 0 | 0 |
| performance_schema | events_statements_history_long | 0 | 0 |
| performance_schema | performance_timers | 0 | 0 |
| performance_schema | file_instances | 0 | 0 |
| performance_schema | events_stages_summary_by_user_by_event_name | 0 | 0 |
| performance_schema | events_stages_history_long | 0 | 0 |
| performance_schema | setup_actors | 0 | 0 |
| performance_schema | cond_instances | 0 | 0 |
| mysql | proxies_priv | 0 | 0 |
| performance_schema | socket_summary_by_instance | 0 | 0 |
| performance_schema | events_statements_current | 0 | 0 |
| mysql | event | 0 | 0 |
| performance_schema | session_connect_attrs | 0 | 0 |
| mysql | plugin | 0 | 0 |
| performance_schema | threads | 0 | 0 |
| mysql | time_zone_transition_type | 0 | 0 |
| mysql | time_zone_name | 0 | 0 |
| performance_schema | file_summary_by_event_name | 0 | 0 |
| performance_schema | events_waits_summary_by_user_by_event_name | 0 | 0 |
| performance_schema | socket_summary_by_event_name | 0 | 0 |
| performance_schema | users | 0 | 0 |
| mysql | servers | 0 | 0 |
| performance_schema | events_waits_summary_by_account_by_event_name | 0 | 0 |
| db01 | tbl_emp | 0 | 0 |
| performance_schema | events_statements_summary_by_host_by_event_name | 0 | 0 |
| db01 | tblA | 0 | 0 |
| performance_schema | table_io_waits_summary_by_index_usage | 0 | 0 |
| performance_schema | events_waits_current | 0 | 0 |
| db01 | user | 0 | 0 |
| mysql | procs_priv | 0 | 0 |
| performance_schema | events_statements_summary_by_thread_by_event_name | 0 | 0 |
| db01 | emp | 0 | 0 |
| db01 | tbl_user | 0 | 0 |
| db01 | test03 | 0 | 0 |
| mysql | slow_log | 0 | 0 |
| performance_schema | file_summary_by_instance | 0 | 0 |
| db01 | article | 0 | 0 |
| performance_schema | objects_summary_global_by_type | 0 | 0 |
| db01 | phone | 0 | 0 |
| performance_schema | events_waits_summary_by_thread_by_event_name | 0 | 0 |
| performance_schema | setup_consumers | 0 | 0 |
| performance_schema | socket_instances | 0 | 0 |
| performance_schema | rwlock_instances | 0 | 0 |
| db01 | tbl_dept | 0 | 0 |
| performance_schema | events_statements_summary_by_user_by_event_name | 0 | 0 |
| db01 | staffs | 0 | 0 |
| db01 | class | 0 | 0 |
| mysql | general_log | 0 | 0 |
| performance_schema | events_stages_summary_global_by_event_name | 0 | 0 |
| performance_schema | events_stages_summary_by_account_by_event_name | 0 | 0 |
| performance_schema | events_statements_summary_by_account_by_event_name | 0 | 0 |
| performance_schema | table_lock_waits_summary_by_table | 0 | 0 |
| performance_schema | hosts | 0 | 0 |
| performance_schema | setup_objects | 0 | 0 |
| performance_schema | events_stages_current | 0 | 0 |
| mysql | time_zone | 0 | 0 |
| mysql | tables_priv | 0 | 0 |
| performance_schema | table_io_waits_summary_by_table | 0 | 0 |
| mysql | time_zone_leap_second | 0 | 0 |
| db01 | book | 0 | 0 |
| performance_schema | session_account_connect_attrs | 0 | 0 |
| db01 | mylock | 0 | 0 |
| mysql | func | 0 | 0 |
| performance_schema | events_statements_summary_global_by_event_name | 0 | 0 |
| performance_schema | events_statements_history | 0 | 0 |
| performance_schema | accounts | 0 | 0 |
| mysql | time_zone_transition | 0 | 0 |
| db01 | dept | 0 | 0 |
| performance_schema | events_stages_summary_by_host_by_event_name | 0 | 0 |
| performance_schema | events_stages_summary_by_thread_by_event_name | 0 | 0 |
| mysql | proc | 0 | 0 |
| performance_schema | setup_instruments | 0 | 0 |
| performance_schema | host_cache | 0 | 0 |
+--------------------+----------------------------------------------------+--------+-------------+
84 rows in set (0.00 sec)
- 添加鎖
lock table 表名1 read(write), 表名2 read(write), ...;
- 釋放表鎖
unlock tables;
2.1.1斥季、讀鎖示例
- 在 session 1 會(huì)話中训桶,給 mylock 表加個(gè)讀鎖
mysql> lock table mylock read;
Query OK, 0 rows affected (0.00 sec)
- 在 session1 會(huì)話中能不能讀取 mylock 表:可以讀
################# session1 中的操作 #################
mysql> select * from mylock;
+----+------+
| id | name |
+----+------+
| 1 | a |
| 2 | b |
| 3 | c |
| 4 | d |
| 5 | e |
+----+------+
5 rows in set (0.00 sec)
- 在 session1 會(huì)話中能不能讀取 book 表:并不行。酣倾。舵揭。
################# session1 中的操作 #################
mysql> select * from book;
ERROR 1100 (HY000): Table 'book' was not locked with LOCK TABLES
- 在 session2 會(huì)話中能不能讀取 mylock 表:可以讀
################# session2 中的操作 #################
mysql> select * from mylock;
+----+------+
| id | name |
+----+------+
| 1 | a |
| 2 | b |
| 3 | c |
| 4 | d |
| 5 | e |
+----+------+
5 rows in set (0.00 sec)
- 在 session1 會(huì)話中能不能修改 mylock 表:并不行。躁锡。午绳。
################# session1 中的操作 #################
mysql> update mylock set name='a2' where id=1;
ERROR 1099 (HY000): Table 'mylock' was locked with a READ lock and can't be updated
- 在 session2 會(huì)話中能不能修改 mylock 表:阻塞,一旦 mylock 表鎖釋放稚铣,則會(huì)執(zhí)行修改操作
################# session2 中的操作 #################
mysql> update mylock set name='a2' where id=1;
# 在這里阻塞著呢~~~
結(jié)論
1.當(dāng)前 session 和其他 session 均可以讀取加了讀鎖的表
2.當(dāng)前 session 不能讀取其他表箱叁,并且不能修改加了讀鎖的表
3.其他 session 想要修改加了讀鎖的表墅垮,必須等待其讀鎖釋放
2.1.2、寫鎖示例
在 session 1 會(huì)話中耕漱,給 mylock 表加個(gè)寫鎖
mysql> lock table mylock write;
Query OK, 0 rows affected (0.00 sec)
- 在 session1 會(huì)話中能不能讀取 mylock 表:闊以
################# session1 中的操作 #################
mysql> select * from mylock;
+----+------+
| id | name |
+----+------+
| 1 | a2 |
| 2 | b |
| 3 | c |
| 4 | d |
| 5 | e |
+----+------+
5 rows in set (0.00 sec)
- 在 session1 會(huì)話中能不能讀取 book 表:不闊以
################# session1 中的操作 #################
mysql> select * from book;
ERROR 1100 (HY000): Table 'book' was not locked with LOCK TABLES
- 在 session1 會(huì)話中能不能修改 mylock 表:當(dāng)然可以啦算色,加寫鎖就是為了修改呀
################# session1 中的操作 #################
mysql> update mylock set name='a2' where id=1;
Query OK, 0 rows affected (0.00 sec)
Rows matched: 1 Changed: 0 Warnings: 0
- 在 session2 會(huì)話中能不能讀取 mylock 表:
################# session2 中的操作 #################
mysql> select * from mylock;
# 在這里阻塞著呢~~~
結(jié)論
1.當(dāng)前 session 不能讀取其他表
2.其他 session 想要讀取加了寫鎖的表,必須等待其讀鎖釋放
3.MyISAM在執(zhí)行查詢語句(SELECT)前螟够,會(huì)自動(dòng)給涉及的所有表加讀鎖灾梦,在執(zhí)行增刪改操作前,會(huì)自動(dòng)給涉及的表加寫鎖妓笙。
4.MySQL的表級(jí)鎖有兩種模式:
- 表共享讀鎖(Table Read Lock)
-
表獨(dú)占寫鎖(Table Write Lock)
結(jié)論:
結(jié)合上表若河,所以對(duì)MyISAM表進(jìn)行操作,會(huì)有以下情況:
1.對(duì)MyISAM表的讀操作(加讀鎖)寞宫,不會(huì)阻塞其他進(jìn)程對(duì)同一表的讀請(qǐng)求萧福,但會(huì)阻塞對(duì)同一表的寫請(qǐng)求。只有當(dāng)讀鎖釋放后辈赋,才會(huì)執(zhí)行其它進(jìn)程的寫操作鲫忍。
2.對(duì)MyISAM表的寫操作(加寫鎖),會(huì)阻塞其他進(jìn)程對(duì)同一表的讀和寫操作钥屈,只有當(dāng)寫鎖釋放后悟民,才會(huì)執(zhí)行其它進(jìn)程的讀寫操作
3.簡而言之,就是讀鎖會(huì)阻塞寫篷就,但是不會(huì)堵塞讀射亏。而寫鎖則會(huì)把讀和寫都堵塞。
2.2竭业、表鎖分析
- 查看哪些表被鎖了智润,0 表示未鎖,1 表示被鎖
show open tables;
【如何分析表鎖定】可以通過檢查table_locks_waited和table_locks_immediate狀態(tài)變量來分析系統(tǒng)上的表鎖定永品,通過 show status like 'table%'; 命令查看
1.Table_locks_immediate:產(chǎn)生表級(jí)鎖定的次數(shù)做鹰,表示可以立即獲取鎖的查詢次數(shù),每立即獲取鎖值加1鼎姐;
2.Table_locks_waited:出現(xiàn)表級(jí)鎖定爭(zhēng)用而發(fā)生等待的次數(shù)(不能立即獲取鎖的次數(shù),每等待一次鎖值加1)更振,此值高則說明存在著較嚴(yán)重的表級(jí)鎖爭(zhēng)用情況炕桨;
mysql> show status like 'table%';
+----------------------------+--------+
| Variable_name | Value |
+----------------------------+--------+
| Table_locks_immediate | 500440 |
| Table_locks_waited | 1 |
| Table_open_cache_hits | 500070 |
| Table_open_cache_misses | 5 |
| Table_open_cache_overflows | 0 |
+----------------------------+--------+
5 rows in set (0.00 sec)
- 此外,Myisam的讀寫鎖調(diào)度是寫優(yōu)先肯腕,這也是myisam不適合做寫為主表的引擎献宫。因?yàn)閷戞i后,其他線程不能做任何操作实撒,大量的更新會(huì)使查詢很難得到鎖姊途,從而造成永遠(yuǎn)阻塞
3涉瘾、行鎖
行鎖的特點(diǎn)
1.偏向InnoDB存儲(chǔ)引擎,開銷大捷兰,加鎖慢立叛;會(huì)出現(xiàn)死鎖;鎖定粒度最小贡茅,發(fā)生鎖沖突的概率最低秘蛇,并發(fā)度也最高。
2.InnoDB與MyISAM的最大不同有兩點(diǎn):一是支持事務(wù)(TRANSACTION)顶考;二是采用了行級(jí)鎖赁还。
3.1、事務(wù)復(fù)習(xí)
行鎖支持事務(wù)驹沿,復(fù)習(xí)下老知識(shí)
事務(wù)(Transation)及其ACID屬性
事務(wù)是由一組SQL語句組成的邏輯處理單元艘策,事務(wù)具有以下4個(gè)屬性,通常簡稱為事務(wù)的ACID屬性渊季。
原子性(Atomicity):事務(wù)是一個(gè)原子操作單元柬焕,其對(duì)數(shù)據(jù)的修改,要么全都執(zhí)行梭域,要么全都不執(zhí)行斑举。
一致性(Consistent):在事務(wù)開始和完成時(shí),數(shù)據(jù)都必須保持一致狀態(tài)病涨。這意味著所有相關(guān)的數(shù)據(jù)規(guī)則都必須應(yīng)用于事務(wù)的修改富玷,以保持?jǐn)?shù)據(jù)的完整性;事務(wù)結(jié)束時(shí)既穆,所有的內(nèi)部數(shù)據(jù)結(jié)構(gòu)(如B樹索引或雙向鏈表)也都必須是正確的赎懦。
隔離性(Isolation):數(shù)據(jù)庫系統(tǒng)提供一定的隔離機(jī)制,保證事務(wù)在不受外部并發(fā)操作影響的“獨(dú)立”環(huán)境執(zhí)行幻工。這意味著事務(wù)處理過程中的中間狀態(tài)對(duì)外部是不可見的励两,反之亦然。
持久性(Durability):事務(wù)院成之后囊颅,它對(duì)于數(shù)據(jù)的修改是永久性的当悔,即使出現(xiàn)系統(tǒng)故障也能夠保持。
并發(fā)事務(wù)處理帶來的問題
1.更新丟失(Lost Update):
- 當(dāng)兩個(gè)或多個(gè)事務(wù)選擇同一行踢代,然后基于最初選定的值更新該行時(shí)盲憎,由于每個(gè)事務(wù)都不知道其他事務(wù)的存在,就會(huì)發(fā)生丟失更新問題一一最后的更新覆蓋了由其他事務(wù)所做的更新胳挎。
- 例如饼疙,兩個(gè)程序員修改同一java文件。每程序員獨(dú)立地更改其副本慕爬,然后保存更改后的副本窑眯,這樣就覆蓋了原始文檔屏积。最后保存其更改副本的編輯人員覆蓋前一個(gè)程序員所做的更改。
- 如果在一個(gè)程序員完成并提交事務(wù)之前磅甩,另一個(gè)程序員不能訪問同一文件炊林,則可避免此問題。
2.臟讀(Dirty Reads):
- 一個(gè)事務(wù)正在對(duì)一條記錄做修改更胖,在這個(gè)事務(wù)完成并提交前铛铁,這條記錄的數(shù)據(jù)就處于不一致狀態(tài);這時(shí)却妨,另一個(gè)事務(wù)也來讀取同一條記錄饵逐,如果不加控制,第二個(gè)事務(wù)讀取了這些“臟”數(shù)據(jù)彪标,并據(jù)此做進(jìn)一步的處理倍权,就會(huì)產(chǎn)生未提交的數(shù)據(jù)依賴關(guān)系。這種現(xiàn)象被形象地叫做”臟讀”捞烟。
- 一句話:事務(wù)A讀取到了事務(wù)B已修改但尚未提交的的數(shù)據(jù)薄声,還在這個(gè)數(shù)據(jù)基礎(chǔ)上做了操作。此時(shí)题画,如果B事務(wù)回滾默辨,A讀取的數(shù)據(jù)無效,不符合一致性要求苍息。
3.不可重復(fù)讀(Non-Repeatable Reads):
- 一個(gè)事務(wù)在讀取某些數(shù)據(jù)后的某個(gè)時(shí)間缩幸,再次讀取以前讀過的數(shù)據(jù),卻發(fā)現(xiàn)其讀出的數(shù)據(jù)已經(jīng)發(fā)生了改變竞思、或某些記錄已經(jīng)被刪除了表谊!這種現(xiàn)象就叫做“不可重復(fù)讀”。
- 一句話:事務(wù)A讀取到了事務(wù)B已經(jīng)提交的修改數(shù)據(jù)盖喷,不符合隔離性
4.幻讀(Phantom Reads):
- 一個(gè)事務(wù)按相同的查詢條件重新讀取以前檢索過的數(shù)據(jù)爆办,卻發(fā)現(xiàn)其他事務(wù)插入了滿足其查詢條件的新數(shù)據(jù),這種現(xiàn)象就稱為“幻讀一句話:事務(wù)A讀取到了事務(wù)B體提交的新增數(shù)據(jù)课梳,不符合隔離性距辆。
- 多說一句:幻讀和臟讀有點(diǎn)類似,臟讀是事務(wù)B里面修改了數(shù)據(jù)惦界,幻讀是事務(wù)B里面新增了數(shù)據(jù)副签。
事物的隔離級(jí)別
1.臟讀”境钟、“不可重復(fù)讀”和“幻讀”,其實(shí)都是數(shù)據(jù)庫讀一致性問題鸯匹,必須由數(shù)據(jù)庫提供一定的事務(wù)隔離機(jī)制來解決雾消。
2.數(shù)據(jù)庫的事務(wù)隔離越嚴(yán)格灾搏,并發(fā)副作用越小挫望,但付出的代價(jià)也就越大,因?yàn)槭聞?wù)隔離實(shí)質(zhì)上就是使事務(wù)在一定程度上“串行化”進(jìn)行狂窑,這顯然與“并發(fā)”是矛盾的媳板。
3.同時(shí),不同的應(yīng)用對(duì)讀一致性和事務(wù)隔離程度的要求也是不同的泉哈,比如許多應(yīng)用對(duì)“不可重復(fù)讀”和“幻讀”并不敏感蛉幸,可能更關(guān)心數(shù)據(jù)并發(fā)訪問的能力。
4.查看當(dāng)前數(shù)據(jù)庫的事務(wù)隔離級(jí)別:show variables like 'tx_isolation'; mysql 默認(rèn)是可重復(fù)讀
3.2丛晦、行鎖案例分析
創(chuàng)建表
- 建表 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);
- test_innodb_lock 表中的測(cè)試數(shù)據(jù)
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)
- test_innodb_lock 表中的索引
mysql> SHOW INDEX FROM test_innodb_lock;
+------------------+------------+------------------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| Table | Non_unique | Key_name | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment |
+------------------+------------+------------------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| test_innodb_lock | 1 | test_innodb_a_ind | 1 | a | A | 9 | NULL | NULL | YES | BTREE | | |
| test_innodb_lock | 1 | test_innodb_lock_b_ind | 1 | b | A | 9 | NULL | NULL | YES | BTREE | | |
+------------------+------------+------------------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
2 rows in set (0.00 sec)
操作同一行數(shù)據(jù)
- session1 開啟事務(wù)奕纫,修改 test_innodb_lock 中的數(shù)據(jù)
mysql> set autocommit=0;
Query OK, 0 rows affected (0.00 sec)
mysql> update test_innodb_lock set b='4001' where a=4;
Query OK, 1 row affected (0.00 sec)
Rows matched: 1 Changed: 1 Warnings: 0
- session2 開啟事務(wù),修改 test_innodb_lock 中同一行數(shù)據(jù)烫沙,將導(dǎo)致 session2 發(fā)生阻塞匹层,一旦 session1 提交事務(wù),session2 將執(zhí)行更新操作
mysql> set autocommit=0;
Query OK, 0 rows affected (0.00 sec)
mysql> update test_innodb_lock set b='4002' where a=4;
# 在這兒阻塞著呢~~~
# 時(shí)間太長锌蓄,會(huì)報(bào)超時(shí)錯(cuò)誤哦
mysql> update test_innodb_lock set b='4001' where a=4;
ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction
操作不同行數(shù)據(jù)
- session1 開啟事務(wù)升筏,修改 test_innodb_lock 中的數(shù)據(jù)
mysql> set autocommit=0;
Query OK, 0 rows affected (0.00 sec)
mysql> update test_innodb_lock set b='4001' where a=4;
Query OK, 0 rows affected (0.00 sec)
Rows matched: 1 Changed: 0 Warnings: 0
- session2 開啟事務(wù),修改 test_innodb_lock 中不同行的數(shù)據(jù)
- 由于采用行鎖瘸爽,session2 和 session1 互不干涉您访,所以 session2 中的修改操作沒有阻塞
mysql> set autocommit=0;
Query OK, 0 rows affected (0.00 sec)
mysql> update test_innodb_lock set b='9001' where a=9;
Query OK, 1 row affected (0.00 sec)
Rows matched: 1 Changed: 1 Warnings: 0
無索引導(dǎo)致行鎖升級(jí)為表鎖
- session1 開啟事務(wù),修改 test_innodb_lock 中的數(shù)據(jù)剪决,varchar 不用 ’ ’ 灵汪,導(dǎo)致系統(tǒng)自動(dòng)轉(zhuǎn)換類型,導(dǎo)致索引失效昼捍,從而進(jìn)行了全表掃描
mysql> set autocommit=0;
Query OK, 0 rows affected (0.00 sec)
mysql> update test_innodb_lock set a=44 where b=4000;
Query OK, 1 row affected (0.00 sec)
Rows matched: 1 Changed: 1 Warnings: 0
- session2 開啟事務(wù)识虚,修改 test_innodb_lock 中不同行的數(shù)據(jù)
- 由于發(fā)生了自動(dòng)類型轉(zhuǎn)換,索引失效妒茬,導(dǎo)致行鎖變?yōu)楸礞i
mysql> set autocommit=0;
Query OK, 0 rows affected (0.00 sec)
mysql> update test_innodb_lock set b='9001' where a=9;
# 在這兒阻塞著呢~~~
3.3担锤、間隙鎖
什么是間隙鎖
1.當(dāng)我們用范圍條件而不是相等條件檢索數(shù)據(jù),并請(qǐng)求共享或排他鎖時(shí)乍钻,InnoDB會(huì)給符合條件的已有數(shù)據(jù)記錄的索引項(xiàng)加鎖肛循;對(duì)于鍵值在條件范圍內(nèi)但并不存在的記錄,叫做“間隙(GAP)”
2.InnoDB也會(huì)對(duì)這個(gè)“間隙”加鎖银择,這種鎖機(jī)制是所謂的間隙鎖(Next-Key鎖)
間隙鎖的危害
1.因?yàn)镼uery執(zhí)行過程中通過過范圍查找的話多糠,他會(huì)鎖定整個(gè)范圍內(nèi)所有的索引鍵值,即使這個(gè)鍵值并不存在浩考。
2.間隙鎖有一個(gè)比較致命的弱點(diǎn)夹孔,就是當(dāng)鎖定一個(gè)范圍鍵值之后,即使某些不存在的鍵值也會(huì)被無辜的鎖定,而造成在鎖定的時(shí)候無法插入鎖定鍵值范圍內(nèi)的任何數(shù)據(jù)搭伤。在某些場(chǎng)景下這可能會(huì)對(duì)性能造成很大的危害
間隙鎖示例
- test_innodb_lock 表中的數(shù)據(jù)
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)
- session1 開啟事務(wù)只怎,執(zhí)行修改 a > 1 and a < 6 的數(shù)據(jù),這會(huì)導(dǎo)致 mysql 將 a = 2 的數(shù)據(jù)行鎖琢(雖然表中并沒有這行數(shù)據(jù))
mysql> set autocommit=0;
Query OK, 0 rows affected (0.00 sec)
mysql> update test_innodb_lock set b='Heygo' where a>1 and a<6;
Query OK, 3 rows affected (0.00 sec)
Rows matched: 3 Changed: 3 Warnings: 0
- session2 開啟事務(wù)身堡,修改 test_innodb_lock 中不同行的數(shù)據(jù),也會(huì)導(dǎo)致阻塞拍鲤,直至 session1 提交事務(wù)
mysql> set autocommit=0;
Query OK, 0 rows affected (0.00 sec)
mysql> update test_innodb_lock set b='9001' where a=9;
# 在這兒阻塞著呢~~~
3.4贴谎、手動(dòng)行鎖
如何鎖定一行
- select xxx ... for update 鎖定某一行后,其它的操作會(huì)被阻塞季稳,直到鎖定行的會(huì)話提交
- session1 開啟事務(wù)擅这,手動(dòng)執(zhí)行 for update 鎖定指定行,待執(zhí)行完指定操作時(shí)再將數(shù)據(jù)提交
mysql> set autocommit=0;
Query OK, 0 rows affected (0.00 sec)
mysql> select * from test_innodb_lock where a=8 for update;
+------+------+
| a | b |
+------+------+
| 8 | 8000 |
+------+------+
1 row in set (0.00 sec)
- session2 開啟事務(wù)绞幌,修改 session1 中被鎖定的行蕾哟,會(huì)導(dǎo)致阻塞,直至 session1 提交事務(wù)
mysql> set autocommit=0;
Query OK, 0 rows affected (0.00 sec)
mysql> update test_innodb_lock set b='XXX' where a=8;
# 在這兒阻塞著呢~
3.5莲蜘、行鎖分析
案例結(jié)論
1.Innodb存儲(chǔ)引擎由于實(shí)現(xiàn)了行級(jí)鎖定谭确,雖然在鎖定機(jī)制的實(shí)現(xiàn)方面所帶來的性能損耗可能比表級(jí)鎖定會(huì)要更高一些,但是在整體并發(fā)處理能力方面要遠(yuǎn)遠(yuǎn)優(yōu)于MyISAM的表級(jí)鎖定的票渠。
2.當(dāng)系統(tǒng)并發(fā)量較高的時(shí)候逐哈,Innodb的整體性能和MyISAM相比就會(huì)有比較明顯的優(yōu)勢(shì)了。
3.但是问顷,Innodb的行級(jí)鎖定同樣也有其脆弱的一面昂秃,當(dāng)我們使用不當(dāng)?shù)臅r(shí)候(索引失效,導(dǎo)致行鎖變表鎖)杜窄,可能會(huì)讓Innodb的整體性能表現(xiàn)不僅不能比MyISAM高肠骆,甚至可能會(huì)更差。
行鎖分析
如何分析行鎖定
- 通過檢查InnoDB_row_lock狀態(tài)變量來分析系統(tǒng)上的行鎖的爭(zhēng)奪情況
show status like 'innodb_row_lock%';
mysql> show status like 'innodb_row_lock%';
+-------------------------------+--------+
| Variable_name | Value |
+-------------------------------+--------+
| Innodb_row_lock_current_waits | 0 |
| Innodb_row_lock_time | 212969 |
| Innodb_row_lock_time_avg | 42593 |
| Innodb_row_lock_time_max | 51034 |
| Innodb_row_lock_waits | 5 |
+-------------------------------+--------+
5 rows in set (0.00 sec)
對(duì)各個(gè)狀態(tài)量的說明如下:
1.Innodb_row_lock_current_waits:當(dāng)前正在等待鎖定的數(shù)量塞耕;
2.Innodb_row_lock_time:從系統(tǒng)啟動(dòng)到現(xiàn)在鎖定總時(shí)間長度蚀腿;
3.Innodb_row_lock_time_avg:每次等待所花平均時(shí)間;
4.Innodb_row_lock_time_max:從系統(tǒng)啟動(dòng)到現(xiàn)在等待最常的一次所花的時(shí)間扫外;
5.Innodb_row_lock_waits:系統(tǒng)啟動(dòng)后到現(xiàn)在總共等待的次數(shù)莉钙;
對(duì)于這5個(gè)狀態(tài)變量,比較重要的主要是
1.Innodb_row_lock_time_avg(等待平均時(shí)長)
2.Innodb_row_lock_waits(等待總次數(shù))
3.Innodb_row_lock_time(等待總時(shí)長)
尤其是當(dāng)?shù)却螖?shù)很高筛谚,而且每次等待時(shí)長也不小的時(shí)候磁玉,我們就需要分析系統(tǒng)中為什么會(huì)有如此多的等待,然后根據(jù)分析結(jié)果著手指定優(yōu)化計(jì)劃驾讲。
3.6蚊伞、行鎖優(yōu)化
優(yōu)化建議
1.盡可能讓所有數(shù)據(jù)檢索都通過索引來完成席赂,避免無索引行鎖升級(jí)為表鎖
2.合理設(shè)計(jì)索引,盡量縮小鎖的范圍
3.盡可能較少檢索條件厚柳,避免間隙鎖
4.盡量控制事務(wù)大小氧枣,減少鎖定資源量和時(shí)間長度
5.盡可能低級(jí)別事務(wù)隔離
4沐兵、頁鎖
1.開銷和加鎖時(shí)間界于表鎖和行鎖之間:會(huì)出現(xiàn)死鎖别垮;
2.鎖定粒度界于表鎖和行鎖之間,并發(fā)度一般扎谎。
3.了解即可