本篇文章為大家展示了innodb中怎么實現(xiàn)一個存儲引擎鎖,內(nèi)容簡明扼要并且容易理解,絕對能使你眼前一亮,通過這篇文章的詳細介紹希望你能有所收獲。
成都創(chuàng)新互聯(lián)公司主營龍馬潭網(wǎng)站建設(shè)的網(wǎng)絡(luò)公司,主營網(wǎng)站建設(shè)方案,成都App制作,龍馬潭h5小程序開發(fā)搭建,龍馬潭網(wǎng)站營銷推廣歡迎龍馬潭等地區(qū)企業(yè)咨詢
數(shù)據(jù)庫對鎖的使用是為了支持對共享資源進行并發(fā)的訪問,提供數(shù)據(jù)的完整性和一致性。innodb存儲引擎提供了表鎖、行鎖和意向鎖用來實現(xiàn)事物在不同粒度上進行上鎖,從而提高數(shù)據(jù)庫的并發(fā)訪問,并且保證數(shù)據(jù)的完整性和一致性。
innodb存儲引擎是通過給索引上的索引項加鎖來實現(xiàn)行鎖,這種特點也就意味著,只要通過索引條件檢索數(shù)據(jù),innodb才會使用行級鎖,否則會使用表鎖。innodb存儲引擎有以下鎖類型:
1.共享鎖和排他鎖(Shared and Exclusive Locks)
2.意向鎖(Intention Locks)
3.記錄鎖(Record Locks)
4.間隙鎖(Gap Locks)
5.Next-Key Locks
6.插入意向鎖(Insert Intention Locks)
7.自增鎖(AUTO-INC Locks)
8.空間索引謂詞鎖(Predicate Locks for Spatial Indexes)
innodb實現(xiàn)標準行級鎖,其中有兩種類型的鎖,共享鎖(S)和獨占鎖(X)。
1.共享鎖,允許一個事務(wù)去讀一行,阻止其他事務(wù)獲得相同數(shù)據(jù)集的排它鎖。
2.排它鎖,允許獲取排他鎖的事務(wù)更新數(shù)據(jù),阻止其他事務(wù)取得相同的數(shù)據(jù)集的共享讀鎖和排他鎖。
如果事務(wù)T1持有一行記錄的共享鎖,那么另一個不同的事務(wù)T2對該行記錄的鎖定如下:
1.如果事務(wù)T2對該行的請求是一個S鎖,那么事務(wù)T1和事務(wù)T2可以共同對該行記錄持有同一把S鎖。
2.如果事務(wù)T2對該行的請求是一個X鎖,那么事務(wù)T2不可能馬上獲得對該行記錄的X鎖,必須要等到事務(wù)T1將該記錄的S鎖釋放,才可以對該行記錄持有X鎖。
3.如果事務(wù)T1持有第 r 行的獨占(X)鎖,那么對于事務(wù)T2對該行記錄的任何一種請求的鎖都不能立即授予。相反,事務(wù)T2必須要等到事務(wù)T1釋放在r 行上的鎖。
innodb存儲引擎支持多種粒度鎖,允許行鎖和表鎖共存。為了在多個粒度級別上進行鎖定,innodb存儲引擎使用意向鎖來實現(xiàn)。意向鎖是表級鎖,它先指明了該事物是那種類型的鎖(共享鎖或者獨占鎖),然后去鎖定表中某行記錄。(我們可以在后面的Gap lock 和 Next-Key Locks 的演示中MySQL 8.0 的information_schema.data_locks 表中顯示的信息看到)
有兩種類型的意向鎖:
1.意向共享鎖(IS),表明事務(wù)在一個表中的單個行上設(shè)置共享鎖。
2.意向獨占鎖(IX),表明事務(wù)在表中的某行設(shè)置獨占鎖。
例如,SELECT … LOCK IN SHARE MODE 是IS,而 SELECT … FOR UPDATE 是IX鎖。
意向鎖的添加方式:
1.在一個事務(wù)對一張表的某行添加S鎖之前,它必須對該表獲取一個IS鎖或者優(yōu)先級更高的鎖。
2.在一個事務(wù)對一張表的某行添加X鎖之前,它必須對該表獲取一個IX鎖。
表級鎖類型兼容性如下圖所示:
如果與現(xiàn)有鎖相兼容,則授予事務(wù)請求的鎖,但如果它與之沖突,則不會,并且該事務(wù)一直等待直到?jīng)_突的現(xiàn)有鎖被釋放。如果所請求的鎖與持有的鎖沖突是不可能被授予,因為這將會導(dǎo)致死鎖,并且返回錯誤。
意向鎖不會阻塞任何請求,除非將這個表鎖住,例如,LOCK TABLE …. WRITE。意向鎖的主要目的是顯示某人正在鎖定一行,或者在鎖定表中的一行數(shù)據(jù)。
Record Lock總是會去鎖定主鍵、非空的唯一性索引對應(yīng)的索引記錄,如果在建innodb表時并沒有創(chuàng)建任何索引,innodb會對6字節(jié)的rowid的主鍵來進行鎖定。Read-Uncommited/RC級別都是使用該方式來進行加鎖。
Record Lock的主要目的:行鎖可以防止不同事務(wù)版本的數(shù)據(jù)修改提交時造成數(shù)據(jù)沖突的情況。
admin@localhost : test 10:53:27> select * from test; +------+------+ | id | xid | +------+------+ | 1 | 2 | | 3 | 3 | +------+------+ 2 rows in set (0.00 sec) admin@localhost : test 10:53:45> show index from test; Empty set (0.01 sec) admin@localhost : test 10:54:05> show create table test; +-------+---------------------------------------------------------------------------------------------------------------------------------------+ | Table | Create Table | +-------+---------------------------------------------------------------------------------------------------------------------------------------+ | test | CREATE TABLE `test` ( `id` int(11) DEFAULT NULL, `xid` int(11) DEFAULT NULL ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin | +-------+---------------------------------------------------------------------------------------------------------------------------------------+ 1 row in set (0.00 sec)
下面我們開啟2個會話session A 、session B 、session C進行測試
可以通過INFORMATION_SCHEMA中的innodb_lock_waits、innodb_locks、innodb_trx查看到鎖的詳細信息
admin@localhost : test 11:10:26> select * from information_schema.innodb_lock_waits; +-------------------+-------------------+-----------------+------------------+ | requesting_trx_id | requested_lock_id | blocking_trx_id | blocking_lock_id | +-------------------+-------------------+-----------------+------------------+ | 17660049 | 17660049:589:3:4 | 17660047 | 17660047:589:3:4 | +-------------------+-------------------+-----------------+------------------+ 1 row in set, 1 warning (0.02 sec) Warning (Code 1681): 'INFORMATION_SCHEMA.INNODB_LOCK_WAITS' is deprecated and will be removed in a future release. admin@localhost : test 11:10:35> select * from information_schema.innodb_locks; +------------------+-------------+-----------+-----------+---------------+-----------------+------------+-----------+----------+----------------+ | lock_id | lock_trx_id | lock_mode | lock_type | lock_table | lock_index | lock_space | lock_page | lock_rec | lock_data | +------------------+-------------+-----------+-----------+---------------+-----------------+------------+-----------+----------+----------------+ | 17660049:589:3:4 | 17660049 | X | RECORD | `test`.`test` | GEN_CLUST_INDEX | 589 | 3 | 4 | 0x000000000400 | | 17660047:589:3:4 | 17660047 | X | RECORD | `test`.`test` | GEN_CLUST_INDEX | 589 | 3 | 4 | 0x000000000400 | +------------------+-------------+-----------+-----------+---------------+-----------------+------------+-----------+----------+----------------+ 2 rows in set, 1 warning (0.00 sec) admin@localhost : test 11:11:31> select * from information_schema.innodb_trx\G *************************** 1. row *************************** trx_id: 17660049 trx_state: LOCK WAIT trx_started: 2018-05-24 11:10:33 trx_requested_lock_id: 17660049:589:3:4 trx_wait_started: 2018-05-24 11:10:33 trx_weight: 3 trx_mysql_thread_id: 15 trx_query: update test set xid=5 where id=3 trx_operation_state: fetching rows trx_tables_in_use: 1 trx_tables_locked: 1 trx_lock_structs: 3 trx_lock_memory_bytes: 1136 trx_rows_locked: 2 trx_rows_modified: 0 trx_concurrency_tickets: 0 trx_isolation_level: READ COMMITTED trx_unique_checks: 1 trx_foreign_key_checks: 1 trx_last_foreign_key_error: NULL trx_adaptive_hash_latched: 0 trx_adaptive_hash_timeout: 0 trx_is_read_only: 0 trx_autocommit_non_locking: 0
從information_schema.innodb_locks可以看出session A的事務(wù)17660047 與 session C 的事務(wù) 17660049 都是對聚集索引上的id=3記錄是上排它鎖,并且session C 的事務(wù) 17660049 在等待session A的事務(wù)17660047 的X鎖對 id = 3記錄的釋放。
Gap Lock:間隙鎖只存在于RR隔離級別下的輔助索引中,只鎖定一個范圍,但不包含記錄本身。如果只鎖定一個范圍,那這個范圍是怎樣的?
Gap Lock的主要目的:間隙鎖避免了別的事務(wù)插入數(shù)據(jù),從而避免了不可重復(fù)讀現(xiàn)象。
Gap Lock的特點:
一個間隙鎖可能間隔一個索引值、多個索引值或者是無窮
間隙鎖是為了平衡性能和并發(fā)的一部分,并且間隙鎖只能在RR隔離級別下使用
對于使用唯一索引查找數(shù)據(jù),是不需要使用間隙鎖,但是并不包含查詢條件中只包含多列中的某些列,唯一索引在這樣的情況下,會使用間隙鎖來鎖定。
不同的事務(wù)可以在一個間隙鎖中持有沖突的鎖。如果事務(wù)A在一個間隙鎖中持有的是共享的間隙鎖(gap S-lock),而事務(wù)B持有事務(wù)A在相同間隙的獨占間隙鎖(gap X-lock)。該類型的鎖沖突間隙鎖是允許的,如果a記錄被從索引上刪除,不同事務(wù)在該記錄上的間隙鎖將被合并。
innodb存儲引擎的間隙鎖范圍是完全禁止操作的,這將意味著其他事務(wù)無法對間隙鎖范圍進行插入操作。間隙鎖不會阻止不同的事務(wù)去獲取同樣的間隙鎖范圍,因此間隙鎖 gap X-lock 和 gap S-lock 的效果是一樣的。
間隙鎖可以被顯式的禁用。將事務(wù)的隔離級別設(shè)置為 READ COMMITTED 或者開啟 innodb_locks_unsafe_for_binlog=ON (已經(jīng)被棄用)。在這樣的情況下,在查詢和索引掃描中禁用間隙鎖,并且只適用于外鍵約束和主鍵檢查。
在使用READ COMMITTED隔離級別或者開啟 innodb_locks_unsafe_for_binlog=ON 都可以顯式的禁用間隙鎖。開啟“semi-consistent” 半一致行讀取后,MySQL 會過濾掉不匹配的行,并且釋放不匹配的行的鎖,并且將過濾后數(shù)據(jù)返回到存儲引擎層去更新。
測試步驟
在RR隔離級別下,創(chuàng)建一張只有輔助索引的t3表,并且對輔助索引的一個范圍使用 for update 查詢,插入包含在范圍中的值,然后分別對范圍的上確界和下確界進行update操作。
admin@localhost : test 03:55:34> set session transaction_isolation='REPEATABLE-READ' ; Query OK, 0 rows affected (0.00 sec) admin@localhost : test 03:29:44> show variables like '%lation'; +---------------+-----------------+ | Variable_name | Value | +---------------+-----------------+ | transaction_isolation | REPEATABLE-READ | +---------------+-----------------+ 1 row in set (0.01 sec) root@localhost : test1 12:12:40> select * from t3; +------+------+ | id | xid | +------+------+ | 1 | 1 | | 2 | 1 | | 4 | 3 | | 7 | 7 | | 10 | 9 | +------+------+ 5 rows in set (0.00 sec) root@localhost : test1 12:08:24> show index from t3; +-------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+ | Table | Non_unique | Key_name | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment | +-------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+ | t3 | 1 | xid | 1 | xid | A | 3 | NULL | NULL | YES | BTREE | | | +-------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+ 1 row in set (0.00 sec)
接下來使用mysql 8.0 中的performance_schema.data_locks 來輔助我們查看這2個事務(wù)具體鎖的那條記錄,和鎖的模式等相關(guān)信息。
root@localhost : (none) 04:48:29> select * from performance_schema.data_locks; +--------+----------------+-----------------------+-----------+----------+---------------+-------------+----------------+-------------------+-----------------+-----------------------+-----------+-----------+-------------+-------------------+ | ENGINE | ENGINE_LOCK_ID | ENGINE_TRANSACTION_ID | THREAD_ID | EVENT_ID | OBJECT_SCHEMA | OBJECT_NAME | PARTITION_NAME | SUBPARTITION_NAME | INDEX_NAME | OBJECT_INSTANCE_BEGIN | LOCK_TYPE | LOCK_MODE | LOCK_STATUS | LOCK_DATA | +--------+----------------+-----------------------+-----------+----------+---------------+-------------+----------------+-------------------+-----------------+-----------------------+-----------+-----------+-------------+-------------------+ | INNODB | 1609:1059 | 1609 | 64 | 63 | test | t | NULL | NULL | NULL | 140152030668888 | TABLE | IX | GRANTED | NULL | | INNODB | 1609:2:5:4 | 1609 | 64 | 63 | test | t | NULL | NULL | xid | 140152030665848 | RECORD | X,GAP | WAITING | 3, 0x000000000202 | | INNODB | 1608:1059 | 1608 | 63 | 88 | test | t | NULL | NULL | NULL | 140152030662936 | TABLE | IX | GRANTED | NULL | | INNODB | 1608:2:5:4 | 1608 | 63 | 88 | test | t | NULL | NULL | xid | 140152030659896 | RECORD | X | GRANTED | 3, 0x000000000202 | | INNODB | 1608:2:4:4 | 1608 | 63 | 88 | test | t | NULL | NULL | GEN_CLUST_INDEX | 140152030660240 | RECORD | X | GRANTED | 0x000000000202 | | INNODB | 1608:2:5:5 | 1608 | 63 | 88 | test | t | NULL | NULL | xid | 140152030660584 | RECORD | X,GAP | GRANTED | 7, 0x000000000203 | +--------+----------------+-----------------------+-----------+----------+---------------+-------------+----------------+-------------------+-----------------+-----------------------+-----------+-----------+-------------+-------------------+ 6 rows in set (0.00 sec)
事務(wù)A 造成的鎖
1、 | INNODB | 1608:1059 | 1608 | 63 | 88 | test | t | NULL | NULL | NULL | 140152030662936 | TABLE | IX | GRANTED | NULL | 2、 | INNODB | 1608:2:5:4 | 1608 | 63 | 88 | test | t | NULL | NULL | xid | 140152030659896 | RECORD | X | GRANTED | 3, 0x000000000202 事務(wù)A對test庫的t表上的輔助索引xid 對xid=3記錄加 RECORD 鎖,0x000000000202 表示其對應(yīng)的6字節(jié)的rowid的位置指針。 3、| INNODB | 1608:2:4:4 | 1608 | 63 | 88 | test | t | NULL | NULL | GEN_CLUST_INDEX | 140152030660240 | RECORD | X | GRANTED | 0x000000000202 | 由于test庫的t表只有一個非唯一的輔助索引xid,會使用6字節(jié)的rowid來作為聚集索引,事務(wù)A造成的鎖會對 輔助索引xid=3 記錄對應(yīng)的聚集索引也加X鎖,相當于將輔助索引xid=3的整條行記錄都上X鎖 4、| INNODB | 1608:2:5:5 | 1608 | 63 | 88 | test | t | NULL | NULL | xid | 140152030660584 | RECORD | X,GAP | GRANTED | 7, 0x000000000203 | 記錄xid=7 上雖然添加的鎖為X,GAP鎖,但是并沒有對輔助索引xid=7對應(yīng)的聚集索引添加X鎖,所以輔助索引xid=7這條記錄并沒有上X鎖,實際上只是用來表示與前一行記錄在 (3,7)區(qū)間構(gòu)成間隙鎖。
事務(wù)B造成的鎖
| INNODB | 1609:1059 | 1609 | 64 | 63 | test | t | NULL | NULL | NULL | 140152030668888 | TABLE | IX | GRANTED | NULL | | INNODB | 1609:2:5:4 | 1609 | 64 | 63 | test | t | NULL | NULL | xid | 140152030665848 | RECORD | X,GAP | WAITING | 3, 0x000000000202 | 事務(wù)B在事務(wù)A已經(jīng)持有鎖的情況下,進行插入(id,xid) ====>(1,2) 發(fā)現(xiàn)(1,3)存在間隙鎖。無法進行插入操作。
Next-Key Lock 是結(jié)合了Gap Lock和Record Lock的合并,其設(shè)計目的主要是為解決RR級別下的幻讀問題。該鎖定方式相對于Gap Lock和Record Lock是帶閉合區(qū)間的范圍鎖定。
以下介紹其特點:
Innodb存儲引擎使用Next-Key Locks 只在 REPEATABLE READ 隔離級別下。
當進行查詢或者索引掃描時,innodb存儲引擎以行鎖的方式進行鎖定,它會將符合條件的索引記錄使用S鎖或者X鎖。因此,行級鎖實際上就是對索引記錄上鎖。Next-Key Locks會影響 gap鎖 的上一個索引記錄,也就是Next-Key Locks是由索引記錄鎖加上gap 鎖組成。如果一個會話,在索引記錄R上有一個共享或者獨占鎖,在索引記錄R與上一個索引記錄之間的間隙,另一個會話不可能插入一個新的索引記錄。
Next-Key Locks在某些情況下可以鎖住索引記錄的最大值和大于最大值的范圍,大于最大索引記錄的范圍稱為 "supremum pseudo-record"偽記錄。
注:官方文檔并沒有介紹對Next-Key Lock介紹太多,所以我們通過以下場景來對Next-Key Lock的特點進行說明。
場景一:RR隔離級別下對一張只有主鍵的表進行操作
主鍵只是由一列構(gòu)成
root@localhost : test1 07:58:18> select * from t1; +----+------+ | id | xid | +----+------+ | 1 | 1 | | 2 | 1 | | 4 | 3 | | 7 | 7 | | 10 | 9 | +----+------+ 5 rows in set (0.00 sec) root@localhost : test1 07:58:10> show index from t1; +-------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+ | Table | Non_unique | Key_name | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment | +-------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+ | t2 | 0 | PRIMARY | 1 | id | A | 4 | NULL | NULL | | BTREE | | | +-------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+ 1 row in set (0.00 sec)
root@localhost : (none) 05:37:52> select * from performance_schema.data_locks; +--------+----------------+-----------------------+-----------+----------+---------------+-------------+----------------+-------------------+------------+-----------------------+-----------+-----------+-------------+-----------+ | ENGINE | ENGINE_LOCK_ID | ENGINE_TRANSACTION_ID | THREAD_ID | EVENT_ID | OBJECT_SCHEMA | OBJECT_NAME | PARTITION_NAME | SUBPARTITION_NAME | INDEX_NAME | OBJECT_INSTANCE_BEGIN | LOCK_TYPE | LOCK_MODE | LOCK_STATUS | LOCK_DATA | +--------+----------------+-----------------------+-----------+----------+---------------+-------------+----------------+-------------------+------------+-----------------------+-----------+-----------+-------------+-----------+ | INNODB | 1611:1060 | 1611 | 63 | 94 | test | t1 | NULL | NULL | NULL | 140152030662936 | TABLE | IX | GRANTED | NULL | | INNODB | 1611:3:4:4 | 1611 | 63 | 94 | test | t1 | NULL | NULL | PRIMARY | 140152030659896 | RECORD | X | GRANTED | 4 | +--------+----------------+-----------------------+-----------+----------+---------------+-------------+----------------+-------------------+------------+-----------------------+-----------+-----------+-------------+-----------+ 2 rows in set (0.00 sec)
事務(wù)A造成的鎖
| INNODB | 1611:1060 | 1611 | 63 | 94 | test | t1 | NULL | NULL | NULL | 140152030662936 | TABLE | IX | GRANTED | NULL | | INNODB | 1611:3:4:4 | 1611 | 63 | 94 | test | t1 | NULL | NULL | PRIMARY | 140152030659896 | RECORD | X | GRANTED | 4 | 由于我們顯示的定義了主鍵可以看到這里L(fēng)OCK_DATA=4就是這個表內(nèi)建的rowid ,說明聚集索引上的id=4的行記錄直接被加了X鎖,也并沒有用到GAP鎖。
該場景與GAP鎖的場景相比較可以發(fā)現(xiàn),在RR隔離級別下當過濾條件的列是主鍵或者唯一索引的情況下,因為該列值都是唯一值,innodb存儲引擎會對Next-Key Lock進行優(yōu)化,Next-Key Lock會降級成為Record Lock。換句話說gap鎖只存在于RR隔離級別下的輔助索引中,主鍵和唯一索引由于本身具有唯一約束,不需要gap鎖,只有record lock
如果主鍵由多列構(gòu)成,但是只使用其中的一列進行查詢呢?
root@localhost : test 05:03:13> select * from t3; +----+-----+------+ | id | xid | name | +----+-----+------+ | 1 | 1 | a | | 2 | 1 | b | | 4 | 3 | c | | 7 | 7 | d | | 10 | 9 | e | +----+-----+------+ 5 rows in set (0.00 sec) root@localhost : test 05:03:18> show index from t3; +-------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+---------+ | Table | Non_unique | Key_name | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment | Visible | +-------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+---------+ | t3 | 0 | PRIMARY | 1 | id | A | 5 | NULL | NULL | | BTREE | | | YES | | t3 | 0 | PRIMARY | 2 | xid | A | 5 | NULL | NULL | | BTREE | | | YES | +-------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+---------+ 2 rows in set (0.29 sec)
root@localhost : (none) 05:05:51> select * from performance_schema.data_locks; +--------+----------------+-----------------------+-----------+----------+---------------+-------------+----------------+-------------------+------------+-----------------------+-----------+-----------+-------------+-----------+ | ENGINE | ENGINE_LOCK_ID | ENGINE_TRANSACTION_ID | THREAD_ID | EVENT_ID | OBJECT_SCHEMA | OBJECT_NAME | PARTITION_NAME | SUBPARTITION_NAME | INDEX_NAME | OBJECT_INSTANCE_BEGIN | LOCK_TYPE | LOCK_MODE | LOCK_STATUS | LOCK_DATA | +--------+----------------+-----------------------+-----------+----------+---------------+-------------+----------------+-------------------+------------+-----------------------+-----------+-----------+-------------+-----------+ | INNODB | 2123:1062 | 2123 | 68 | 40 | test | t3 | NULL | NULL | NULL | 139846618228824 | TABLE | IX | GRANTED | NULL | | INNODB | 2123:5:4:4 | 2123 | 68 | 40 | test | t3 | NULL | NULL | PRIMARY | 139846618225784 | RECORD | X | GRANTED | 4, 3 | | INNODB | 2123:5:4:5 | 2123 | 68 | 40 | test | t3 | NULL | NULL | PRIMARY | 139846618226128 | RECORD | X,GAP | GRANTED | 7, 7 | +--------+----------------+-----------------------+-----------+----------+---------------+-------------+----------------+-------------------+------------+-----------------------+-----------+-----------+-------------+-----------+ 3 rows in set (0.00 sec)
我們看到了什么?可以發(fā)現(xiàn)當主鍵由多列構(gòu)成時,我們只使用主鍵列中的一列進行查詢時,依然使用到了Next_Key Lock ,為什么這樣?
我們都知道主鍵的鍵值是唯一的,但是我們這里定義的主鍵是primary key(id,xid) 表示的是(id,xid) 組成的鍵值是唯一的,并不能保證id或者xid的鍵值是唯一的,所以這里依然使用Next_Key Lock 來進行加鎖并沒有降級使用Record lock 來進行加鎖。
那當使用主鍵所有列進行查詢時是什么樣子的?
root@localhost : (none) 05:08:52> select * from performance_schema.data_locks; +--------+----------------+-----------------------+-----------+----------+---------------+-------------+----------------+-------------------+------------+-----------------------+-----------+-----------+-------------+-----------+ | ENGINE | ENGINE_LOCK_ID | ENGINE_TRANSACTION_ID | THREAD_ID | EVENT_ID | OBJECT_SCHEMA | OBJECT_NAME | PARTITION_NAME | SUBPARTITION_NAME | INDEX_NAME | OBJECT_INSTANCE_BEGIN | LOCK_TYPE | LOCK_MODE | LOCK_STATUS | LOCK_DATA | +--------+----------------+-----------------------+-----------+----------+---------------+-------------+----------------+-------------------+------------+-----------------------+-----------+-----------+-------------+-----------+ | INNODB | 2125:1062 | 2125 | 68 | 44 | test | t3 | NULL | NULL | NULL | 139846618228824 | TABLE | IX | GRANTED | NULL | | INNODB | 2125:5:4:4 | 2125 | 68 | 44 | test | t3 | NULL | NULL | PRIMARY | 139846618225784 | RECORD | X | GRANTED | 4, 3 | +--------+----------------+-----------------------+-----------+----------+---------------+-------------+----------------+-------------------+------------+-----------------------+-----------+-----------+-------------+-----------+ 2 rows in set (0.00 sec)
可以看到當我們使用主鍵的所有列進行查詢時Next_Key Lock 降級為Record Lock 。
場景二:RR隔離級別下對一張只有非唯一索引的表做操作
在RR隔離級別下,當表中只有一個索引并且為非唯一索引條件上進行等值當前讀或者范圍當前讀時,其加鎖是怎樣的?
admin@localhost : test 03:55:34> set session transaction_isolation='REPEATABLE-READ' ; Query OK, 0 rows affected (0.00 sec) admin@localhost : test 03:29:44> show variables like '%lation'; +---------------+-----------------+ | Variable_name | Value | +---------------+-----------------+ | transaction_isolation| REPEATABLE-READ | +---------------+-----------------+ 1 row in set (0.01 sec) root@localhost : test1 12:12:40> select * from t; +------+------+ | id | xid | +------+------+ | 1 | 1 | | 2 | 1 | | 4 | 3 | | 7 | 7 | | 10 | 9 | +------+------+ 5 rows in set (0.00 sec) root@localhost : test1 12:08:24> show index from t; +-------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+ | Table | Non_unique | Key_name | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment | +-------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+ | t3 | 1 | xid | 1 | xid | A | 3 | NULL | NULL | YES | BTREE | | | +-------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+ 1 row in set (0.00 sec)
等值當前讀
root@localhost : (none) 06:51:25> select * from performance_schema.data_locks; +--------+----------------+-----------------------+-----------+----------+---------------+-------------+----------------+-------------------+-----------------+-----------------------+-----------+-----------+-------------+-------------------+ | ENGINE | ENGINE_LOCK_ID | ENGINE_TRANSACTION_ID | THREAD_ID | EVENT_ID | OBJECT_SCHEMA | OBJECT_NAME | PARTITION_NAME | SUBPARTITION_NAME | INDEX_NAME | OBJECT_INSTANCE_BEGIN | LOCK_TYPE | LOCK_MODE | LOCK_STATUS | LOCK_DATA | +--------+----------------+-----------------------+-----------+----------+---------------+-------------+----------------+-------------------+-----------------+-----------------------+-----------+-----------+-------------+-------------------+ | INNODB | 1622:1059 | 1622 | 64 | 86 | test | t | NULL | NULL | NULL | 140152030668888 | TABLE | IX | GRANTED | NULL | | INNODB | 1622:2:5:5 | 1622 | 64 | 86 | test | t | NULL | NULL | xid | 140152030665848 | RECORD | X,GAP | WAITING | 7, 0x000000000203 | | INNODB | 1621:1059 | 1621 | 63 | 111 | test | t | NULL | NULL | NULL | 140152030662936 | TABLE | IX | GRANTED | NULL | | INNODB | 1621:2:5:4 | 1621 | 63 | 111 | test | t | NULL | NULL | xid | 140152030659896 | RECORD | X | GRANTED | 3, 0x000000000202 | | INNODB | 1621:2:4:4 | 1621 | 63 | 111 | test | t | NULL | NULL | GEN_CLUST_INDEX | 140152030660240 | RECORD | X | GRANTED | 0x000000000202 | | INNODB | 1621:2:5:5 | 1621 | 63 | 111 | test | t | NULL | NULL | xid | 140152030660584 | RECORD | X,GAP | GRANTED | 7, 0x000000000203 | +--------+----------------+-----------------------+-----------+----------+---------------+-------------+----------------+-------------------+-----------------+-----------------------+-----------+-----------+-------------+-------------------+ 6 rows in set (0.00 sec)
事務(wù)A造成的鎖
| INNODB | 1621:1059 | 1621 | 63 | 111 | test | t | NULL | NULL | NULL | 140152030662936 | TABLE | IX | GRANTED | NULL | | INNODB | 1621:2:5:4 | 1621 | 63 | 111 | test | t | NULL | NULL | xid | 140152030659896 | RECORD | X | GRANTED | 3, 0x000000000202 | | INNODB | 1621:2:4:4 | 1621 | 63 | 111 | test | t | NULL | NULL | GEN_CLUST_INDEX | 140152030660240 | RECORD | X | GRANTED | 0x000000000202 | | INNODB | 1621:2:5:5 | 1621 | 63 | 111 | test | t | NULL | NULL | xid | 140152030660584 | RECORD | X,GAP | GRANTED | 7, 0x000000000203 |
事務(wù)B造成的鎖
| INNODB | 1622:1059 | 1622 | 64 | 86 | test | t | NULL | NULL | NULL | 140152030668888 | TABLE | IX | GRANTED | NULL | | INNODB | 1622:2:5:5 | 1622 | 64 | 86 | test | t | NULL | NULL | xid | 140152030665848 | RECORD | X,GAP | WAITING | 7, 0x000000000203 | 由于事務(wù)A在輔助索引記錄 (3,7)之間是間隙鎖,而事務(wù)B插入的數(shù)據(jù)(id,xid) ===>(5,5) 中的xid=5在(3,7) 間隙鎖范圍中,所以才會顯示LOCK_MODE =X,GAP LOCK_STATUS=WAITING LOCK_DATA = 7, 0x000000000203 會等待事務(wù)A將鎖釋放直至超時。
事務(wù)C說明事務(wù)A造成的間隙鎖實際上并沒有將輔助索引記錄xid=7也鎖住,不包含記錄xid=7。
事務(wù)A的加鎖方式下圖所示:
范圍當前讀
root@localhost : (none) 07:08:05> select * from performance_schema.data_locks; +--------+----------------+-----------------------+-----------+----------+---------------+-------------+----------------+-------------------+-----------------+-----------------------+-----------+-----------+-------------+-------------------+ | ENGINE | ENGINE_LOCK_ID | ENGINE_TRANSACTION_ID | THREAD_ID | EVENT_ID | OBJECT_SCHEMA | OBJECT_NAME | PARTITION_NAME | SUBPARTITION_NAME | INDEX_NAME | OBJECT_INSTANCE_BEGIN | LOCK_TYPE | LOCK_MODE | LOCK_STATUS | LOCK_DATA | +--------+----------------+-----------------------+-----------+----------+---------------+-------------+----------------+-------------------+-----------------+-----------------------+-----------+-----------+-------------+-------------------+ | INNODB | 1624:1059 | 1624 | 64 | 93 | test | t | NULL | NULL | NULL | 140152030668888 | TABLE | IX | GRANTED | NULL | | INNODB | 1624:2:5:5 | 1624 | 64 | 93 | test | t | NULL | NULL | xid | 140152030665848 | RECORD | X | WAITING | 7, 0x000000000203 | | INNODB | 1623:1059 | 1623 | 63 | 115 | test | t | NULL | NULL | NULL | 140152030662936 | TABLE | IX | GRANTED | NULL | | INNODB | 1623:2:5:4 | 1623 | 63 | 115 | test | t | NULL | NULL | xid | 140152030659896 | RECORD | X | GRANTED | 3, 0x000000000202 | | INNODB | 1623:2:5:5 | 1623 | 63 | 115 | test | t | NULL | NULL | xid | 140152030659896 | RECORD | X | GRANTED | 7, 0x000000000203 | | INNODB | 1623:2:4:4 | 1623 | 63 | 115 | test | t | NULL | NULL | GEN_CLUST_INDEX | 140152030660240 | RECORD | X | GRANTED | 0x000000000202 | +--------+----------------+-----------------------+-----------+----------+---------------+-------------+----------------+-------------------+-----------------+-----------------------+-----------+-----------+-------------+-------------------+ 6 rows in set (0.00 sec)
事務(wù)A 造成的鎖
1、| INNODB | 1623:1059 | 1623 | 63 | 115 | test | t | NULL | NULL | NULL | 140152030662936 | TABLE | IX | GRANTED | NULL | 2、| INNODB | 1623:2:5:4 | 1623 | 63 | 115 | test | t | NULL | NULL | xid | 140152030659896 | RECORD | X | GRANTED | 3, 0x000000000202 | 3、| INNODB | 1623:2:5:5 | 1623 | 63 | 115 | test | t | NULL | NULL | xid | 140152030659896 | RECORD | X | GRANTED | 7, 0x000000000203 | 與 select * from t where xid=3 for update; 當前讀相比通過mysql 8.0的performance_schema.data_locks表可以看到輔助索引記錄xid=7 的LOCK_MODE =X,GAP 其不會鎖定輔助索引xid=7記錄,而范圍當前讀會將其鎖住 4、| INNODB | 1623:2:4:4 | 1623 | 63 | 115 | test | t | NULL | NULL | GEN_CLUST_INDEX | 140152030660240 | RECORD | X | GRANTED | 0x000000000202 | 。
如果是select * from t where xid>1 and xid<=7 for update; 又是怎么樣的呢?
root@localhost : (none) 09:49:17> select * from performance_schema.data_locks; +--------+----------------+-----------------------+-----------+----------+---------------+-------------+----------------+-------------------+-----------------+-----------------------+-----------+-----------+-------------+-------------------+ | ENGINE | ENGINE_LOCK_ID | ENGINE_TRANSACTION_ID | THREAD_ID | EVENT_ID | OBJECT_SCHEMA | OBJECT_NAME | PARTITION_NAME | SUBPARTITION_NAME | INDEX_NAME | OBJECT_INSTANCE_BEGIN | LOCK_TYPE | LOCK_MODE | LOCK_STATUS | LOCK_DATA | +--------+----------------+-----------------------+-----------+----------+---------------+-------------+----------------+-------------------+-----------------+-----------------------+-----------+-----------+-------------+-------------------+ | INNODB | 2061:1059 | 2061 | 64 | 24 | test | t | NULL | NULL | NULL | 139846618228824 | TABLE | IX | GRANTED | NULL | | INNODB | 2061:2:5:4 | 2061 | 64 | 24 | test | t | NULL | NULL | xid | 139846618225784 | RECORD | X | GRANTED | 3, 0x000000000202 | | INNODB | 2061:2:5:5 | 2061 | 64 | 24 | test | t | NULL | NULL | xid | 139846618225784 | RECORD | X | GRANTED | 7, 0x000000000203 | | INNODB | 2061:2:5:6 | 2061 | 64 | 24 | test | t | NULL | NULL | xid | 139846618225784 | RECORD | X | GRANTED | 9, 0x000000000205 | | INNODB | 2061:2:4:4 | 2061 | 64 | 24 | test | t | NULL | NULL | GEN_CLUST_INDEX | 139846618226128 | RECORD | X | GRANTED | 0x000000000202 | | INNODB | 2061:2:4:5 | 2061 | 64 | 24 | test | t | NULL | NULL | GEN_CLUST_INDEX | 139846618226128 | RECORD | X | GRANTED | 0x000000000203 | +--------+----------------+-----------------------+-----------+----------+---------------+-------------+----------------+-------------------+-----------------+-----------------------+-----------+-----------+-------------+-------------------+ 6 rows in set (0.62 sec)
其加鎖下圖所示:
說明:由于該表沒有主鍵只有一個非唯一的輔助索引key(xid ),使用6字節(jié)rowid做聚集索引。
如果是select * from t where xid>3 for update; 又是怎么樣的呢?
root@localhost : test 03:25:06> select * from performance_schema.data_locks; +--------+----------------+-----------------------+-----------+----------+---------------+-------------+----------------+-------------------+-----------------+-----------------------+-----------+-----------+-------------+------------------------+ | ENGINE | ENGINE_LOCK_ID | ENGINE_TRANSACTION_ID | THREAD_ID | EVENT_ID | OBJECT_SCHEMA | OBJECT_NAME | PARTITION_NAME | SUBPARTITION_NAME | INDEX_NAME | OBJECT_INSTANCE_BEGIN | LOCK_TYPE | LOCK_MODE | LOCK_STATUS | LOCK_DATA | +--------+----------------+-----------------------+-----------+----------+---------------+-------------+----------------+-------------------+-----------------+-----------------------+-----------+-----------+-------------+------------------------+ | INNODB | 2100:1059 | 2100 | 65 | 46 | test | t | NULL | NULL | NULL | 139846618246760 | TABLE | IX | GRANTED | NULL | | INNODB | 2100:2:5:1 | 2100 | 65 | 46 | test | t | NULL | NULL | xid | 139846618243720 | RECORD | X | WAITING | supremum pseudo-record | | INNODB | 2099:1059 | 2099 | 67 | 28 | test | t | NULL | NULL | NULL | 139846618240760 | TABLE | IX | GRANTED | NULL | | INNODB | 2099:2:5:6 | 2099 | 67 | 28 | test | t | NULL | NULL | xid | 139846618237880 | RECORD | X,GAP | WAITING | 9, 0x000000000205 | | INNODB | 2098:1059 | 2098 | 66 | 40 | test | t | NULL | NULL | NULL | 139846618234776 | TABLE | IX | GRANTED | NULL | | INNODB | 2098:2:5:5 | 2098 | 66 | 40 | test | t | NULL | NULL | xid | 139846618231848 | RECORD | X,GAP | WAITING | 7, 0x000000000203 | | INNODB | 2097:1059 | 2097 | 64 | 75 | test | t | NULL | NULL | NULL | 139846618228824 | TABLE | IX | GRANTED | NULL | | INNODB | 2097:2:5:1 | 2097 | 64 | 75 | test | t | NULL | NULL | xid | 139846618225784 | RECORD | X | GRANTED | supremum pseudo-record | | INNODB | 2097:2:5:5 | 2097 | 64 | 75 | test | t | NULL | NULL | xid | 139846618225784 | RECORD | X | GRANTED | 7, 0x000000000203 | | INNODB | 2097:2:5:6 | 2097 | 64 | 75 | test | t | NULL | NULL | xid | 139846618225784 | RECORD | X | GRANTED | 9, 0x000000000205 | | INNODB | 2097:2:4:5 | 2097 | 64 | 75 | test | t | NULL | NULL | GEN_CLUST_INDEX | 139846618226128 | RECORD | X | GRANTED | 0x000000000203 | | INNODB | 2097:2:4:6 | 2097 | 64 | 75 | test | t | NULL | NULL | GEN_CLUST_INDEX | 139846618226128 | RECORD | X | GRANTED | 0x000000000205 | +--------+----------------+-----------------------+-----------+----------+---------------+-------------+----------------+-------------------+-----------------+-----------------------+-----------+-----------+-------------+------------------------+ 12 rows in set (0.00 sec)
事務(wù)A造成的鎖
| INNODB | 2097:1059 | 2097 | 64 | 75 | test | t | NULL | NULL | NULL | 139846618228824 | TABLE | IX | GRANTED | NULL | | INNODB | 2097:2:5:1 | 2097 | 64 | 75 | test | t | NULL | NULL | xid | 139846618225784 | RECORD | X | GRANTED | supremum pseudo-record | 這里可以看到與之前的不同,上確界添加了X鎖,由于使用 select * from t where xid>3 for update; 該表的xid的上確界為9,它需要將9到正無窮也要鎖住,supremum pseudo-record 上確界偽記錄 | INNODB | 2097:2:5:5 | 2097 | 64 | 75 | test | t | NULL | NULL | xid | 139846618225784 | RECORD | X | GRANTED | 7, 0x000000000203 | | INNODB | 2097:2:5:6 | 2097 | 64 | 75 | test | t | NULL | NULL | xid | 139846618225784 | RECORD | X | GRANTED | 9, 0x000000000205 | | INNODB | 2097:2:4:5 | 2097 | 64 | 75 | test | t | NULL | NULL | GEN_CLUST_INDEX | 139846618226128 | RECORD | X | GRANTED | 0x000000000203 | | INNODB | 2097:2:4:6 | 2097 | 64 | 75 | test | t | NULL | NULL | GEN_CLUST_INDEX | 139846618226128 | RECORD | X | GRANTED | 0x000000000205 |
下圖所示:
在RR隔離級別下,對一張只有非唯一輔助索引等值當前讀和范圍當前讀造成的鎖,以t表為例。
我們可以得出以下結(jié)論:
在RR隔離級別下,如果表中只有一個非唯一的輔助索引,當進行等值當前讀時會與下一行記錄形成間隙鎖,但不會鎖住下一行記錄;范圍當前讀時會與下一行記錄形成間隙鎖并且會鎖住該行。
那么可能有人會問在RR隔離級別下,為什么在對輔助索引做范圍當前讀時會與下一行記錄形成間隙鎖并且還會鎖住該行呢?
因為對于非唯一的輔助索引不能保證鍵值的唯一,所以需要鎖住滿足條件的下一行進行判斷,當然這里還有查詢優(yōu)化器的功勞。
場景三:RR隔離級別下對一張有主鍵和輔助索引的表做操作
root@localhost : test 10:20:09> select * from t2; +----+------+ | id | xid | +----+------+ | 1 | 1 | | 2 | 1 | | 4 | 3 | | 7 | 7 | | 10 | 9 | +----+------+ 5 rows in set (0.00 sec) root@localhost : test 10:20:02> show index from t2; +-------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+---------+ | Table | Non_unique | Key_name | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment | Visible | +-------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+---------+ | t2 | 0 | PRIMARY | 1 | id | A | 5 | NULL | NULL | | BTREE | | | YES | | t2 | 1 | xid | 1 | xid | A | 4 | NULL | NULL | YES | BTREE | | | YES | +-------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+---------+ 2 rows in set (0.04 sec)
等值當前讀
root@localhost : (none) 10:29:14> select * from performance_schema.data_locks; +--------+----------------+-----------------------+-----------+----------+---------------+-------------+----------------+-------------------+------------+-----------------------+-----------+-----------+-------------+-----------+ | ENGINE | ENGINE_LOCK_ID | ENGINE_TRANSACTION_ID | THREAD_ID | EVENT_ID | OBJECT_SCHEMA | OBJECT_NAME | PARTITION_NAME | SUBPARTITION_NAME | INDEX_NAME | OBJECT_INSTANCE_BEGIN | LOCK_TYPE | LOCK_MODE | LOCK_STATUS | LOCK_DATA | +--------+----------------+-----------------------+-----------+----------+---------------+-------------+----------------+-------------------+------------+-----------------------+-----------+-----------+-------------+-----------+ | INNODB | 2070:1061 | 2070 | 65 | 14 | test | t2 | NULL | NULL | NULL | 139846618246760 | TABLE | IX | GRANTED | NULL | | INNODB | 2070:4:4:5 | 2070 | 65 | 14 | test | t2 | NULL | NULL | PRIMARY | 139846618243720 | RECORD | X | GRANTED | 7 | | INNODB | 2069:1061 | 2069 | 67 | 18 | test | t2 | NULL | NULL | NULL | 139846618240760 | TABLE | IX | GRANTED | NULL | | INNODB | 2069:4:5:5 | 2069 | 67 | 18 | test | t2 | NULL | NULL | xid | 139846618237880 | RECORD | X,GAP | WAITING | 7, 7 | | INNODB | 2068:1061 | 2068 | 66 | 22 | test | t2 | NULL | NULL | NULL | 139846618234776 | TABLE | IX | GRANTED | NULL | | INNODB | 2068:4:5:4 | 2068 | 66 | 22 | test | t2 | NULL | NULL | xid | 139846618231848 | RECORD | X,GAP | WAITING | 3, 4 | | INNODB | 2065:1061 | 2065 | 64 | 43 | test&n當前題目:innodb中怎么實現(xiàn)一個存儲引擎鎖
URL網(wǎng)址:http://www.rwnh.cn/article4/jepjoe.html成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供外貿(mào)建站、軟件開發(fā)、網(wǎng)站導(dǎo)航、網(wǎng)站收錄、營銷型網(wǎng)站建設(shè)、面包屑導(dǎo)航
聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請盡快告知,我們將會在第一時間刪除。文章觀點不代表本網(wǎng)站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時需注明來源: 創(chuàng)新互聯(lián)