MySQL 鎖機(jī)制

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.了解即可

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末碳想,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子毁靶,更是在濱河造成了極大的恐慌胧奔,老刑警劉巖,帶你破解...
    沈念sama閱讀 210,978評(píng)論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件预吆,死亡現(xiàn)場(chǎng)離奇詭異龙填,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)拐叉,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 89,954評(píng)論 2 384
  • 文/潘曉璐 我一進(jìn)店門岩遗,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人凤瘦,你說我怎么就攤上這事宿礁。” “怎么了蔬芥?”我有些...
    開封第一講書人閱讀 156,623評(píng)論 0 345
  • 文/不壞的土叔 我叫張陵梆靖,是天一觀的道長。 經(jīng)常有香客問我笔诵,道長返吻,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,324評(píng)論 1 282
  • 正文 為了忘掉前任乎婿,我火速辦了婚禮测僵,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘次酌。我一直安慰自己恨课,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,390評(píng)論 5 384
  • 文/花漫 我一把揭開白布岳服。 她就那樣靜靜地躺著剂公,像睡著了一般。 火紅的嫁衣襯著肌膚如雪吊宋。 梳的紋絲不亂的頭發(fā)上纲辽,一...
    開封第一講書人閱讀 49,741評(píng)論 1 289
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼拖吼。 笑死鳞上,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的吊档。 我是一名探鬼主播篙议,決...
    沈念sama閱讀 38,892評(píng)論 3 405
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼怠硼!你這毒婦竟也來了鬼贱?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,655評(píng)論 0 266
  • 序言:老撾萬榮一對(duì)情侶失蹤香璃,失蹤者是張志新(化名)和其女友劉穎这难,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體葡秒,經(jīng)...
    沈念sama閱讀 44,104評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡姻乓,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,451評(píng)論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了眯牧。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片蹋岩。...
    茶點(diǎn)故事閱讀 38,569評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖炸站,靈堂內(nèi)的尸體忽然破棺而出星澳,到底是詐尸還是另有隱情,我是刑警寧澤旱易,帶...
    沈念sama閱讀 34,254評(píng)論 4 328
  • 正文 年R本政府宣布禁偎,位于F島的核電站,受9級(jí)特大地震影響阀坏,放射性物質(zhì)發(fā)生泄漏如暖。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,834評(píng)論 3 312
  • 文/蒙蒙 一忌堂、第九天 我趴在偏房一處隱蔽的房頂上張望盒至。 院中可真熱鬧,春花似錦士修、人聲如沸枷遂。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,725評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽酒唉。三九已至,卻和暖如春沸移,著一層夾襖步出監(jiān)牢的瞬間痪伦,已是汗流浹背侄榴。 一陣腳步聲響...
    開封第一講書人閱讀 31,950評(píng)論 1 264
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留网沾,地道東北人癞蚕。 一個(gè)月前我還...
    沈念sama閱讀 46,260評(píng)論 2 360
  • 正文 我出身青樓,卻偏偏與公主長得像辉哥,于是被迫代替她去往敵國和親桦山。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,446評(píng)論 2 348

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