您好,登錄后才能下訂單哦!
本篇文章為大家展示了innodb中怎么實現一個存儲引擎鎖,內容簡明扼要并且容易理解,絕對能使你眼前一亮,通過這篇文章的詳細介紹希望你能有所收獲。
數據庫對鎖的使用是為了支持對共享資源進行并發的訪問,提供數據的完整性和一致性。innodb存儲引擎提供了表鎖、行鎖和意向鎖用來實現事物在不同粒度上進行上鎖,從而提高數據庫的并發訪問,并且保證數據的完整性和一致性。
innodb存儲引擎是通過給索引上的索引項加鎖來實現行鎖,這種特點也就意味著,只要通過索引條件檢索數據,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實現標準行級鎖,其中有兩種類型的鎖,共享鎖(S)和獨占鎖(X)。
1.共享鎖,允許一個事務去讀一行,阻止其他事務獲得相同數據集的排它鎖。
2.排它鎖,允許獲取排他鎖的事務更新數據,阻止其他事務取得相同的數據集的共享讀鎖和排他鎖。
如果事務T1持有一行記錄的共享鎖,那么另一個不同的事務T2對該行記錄的鎖定如下:
1.如果事務T2對該行的請求是一個S鎖,那么事務T1和事務T2可以共同對該行記錄持有同一把S鎖。
2.如果事務T2對該行的請求是一個X鎖,那么事務T2不可能馬上獲得對該行記錄的X鎖,必須要等到事務T1將該記錄的S鎖釋放,才可以對該行記錄持有X鎖。
3.如果事務T1持有第 r 行的獨占(X)鎖,那么對于事務T2對該行記錄的任何一種請求的鎖都不能立即授予。相反,事務T2必須要等到事務T1釋放在r 行上的鎖。
innodb存儲引擎支持多種粒度鎖,允許行鎖和表鎖共存。為了在多個粒度級別上進行鎖定,innodb存儲引擎使用意向鎖來實現。意向鎖是表級鎖,它先指明了該事物是那種類型的鎖(共享鎖或者獨占鎖),然后去鎖定表中某行記錄。(我們可以在后面的Gap lock 和 Next-Key Locks 的演示中MySQL 8.0 的information_schema.data_locks 表中顯示的信息看到)
有兩種類型的意向鎖:
1.意向共享鎖(IS),表明事務在一個表中的單個行上設置共享鎖。
2.意向獨占鎖(IX),表明事務在表中的某行設置獨占鎖。
例如,SELECT … LOCK IN SHARE MODE 是IS,而 SELECT … FOR UPDATE 是IX鎖。
意向鎖的添加方式:
1.在一個事務對一張表的某行添加S鎖之前,它必須對該表獲取一個IS鎖或者優先級更高的鎖。
2.在一個事務對一張表的某行添加X鎖之前,它必須對該表獲取一個IX鎖。
表級鎖類型兼容性如下圖所示:
如果與現有鎖相兼容,則授予事務請求的鎖,但如果它與之沖突,則不會,并且該事務一直等待直到沖突的現有鎖被釋放。如果所請求的鎖與持有的鎖沖突是不可能被授予,因為這將會導致死鎖,并且返回錯誤。
意向鎖不會阻塞任何請求,除非將這個表鎖住,例如,LOCK TABLE …. WRITE。意向鎖的主要目的是顯示某人正在鎖定一行,或者在鎖定表中的一行數據。
Record Lock總是會去鎖定主鍵、非空的唯一性索引對應的索引記錄,如果在建innodb表時并沒有創建任何索引,innodb會對6字節的rowid的主鍵來進行鎖定。Read-Uncommited/RC級別都是使用該方式來進行加鎖。
Record Lock的主要目的:行鎖可以防止不同事務版本的數據修改提交時造成數據沖突的情況。
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的事務17660047 與 session C 的事務 17660049 都是對聚集索引上的id=3記錄是上排它鎖,并且session C 的事務 17660049 在等待session A的事務17660047 的X鎖對 id = 3記錄的釋放。
Gap Lock:間隙鎖只存在于RR隔離級別下的輔助索引中,只鎖定一個范圍,但不包含記錄本身。如果只鎖定一個范圍,那這個范圍是怎樣的?
Gap Lock的主要目的:間隙鎖避免了別的事務插入數據,從而避免了不可重復讀現象。
Gap Lock的特點:
一個間隙鎖可能間隔一個索引值、多個索引值或者是無窮
間隙鎖是為了平衡性能和并發的一部分,并且間隙鎖只能在RR隔離級別下使用
對于使用唯一索引查找數據,是不需要使用間隙鎖,但是并不包含查詢條件中只包含多列中的某些列,唯一索引在這樣的情況下,會使用間隙鎖來鎖定。
不同的事務可以在一個間隙鎖中持有沖突的鎖。如果事務A在一個間隙鎖中持有的是共享的間隙鎖(gap S-lock),而事務B持有事務A在相同間隙的獨占間隙鎖(gap X-lock)。該類型的鎖沖突間隙鎖是允許的,如果a記錄被從索引上刪除,不同事務在該記錄上的間隙鎖將被合并。
innodb存儲引擎的間隙鎖范圍是完全禁止操作的,這將意味著其他事務無法對間隙鎖范圍進行插入操作。間隙鎖不會阻止不同的事務去獲取同樣的間隙鎖范圍,因此間隙鎖 gap X-lock 和 gap S-lock 的效果是一樣的。
間隙鎖可以被顯式的禁用。將事務的隔離級別設置為 READ COMMITTED 或者開啟 innodb_locks_unsafe_for_binlog=ON (已經被棄用)。在這樣的情況下,在查詢和索引掃描中禁用間隙鎖,并且只適用于外鍵約束和主鍵檢查。
在使用READ COMMITTED隔離級別或者開啟 innodb_locks_unsafe_for_binlog=ON 都可以顯式的禁用間隙鎖。開啟“semi-consistent” 半一致行讀取后,MySQL 會過濾掉不匹配的行,并且釋放不匹配的行的鎖,并且將過濾后數據返回到存儲引擎層去更新。
測試步驟
在RR隔離級別下,創建一張只有輔助索引的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個事務具體鎖的那條記錄,和鎖的模式等相關信息。
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)
事務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 事務A對test庫的t表上的輔助索引xid 對xid=3記錄加 RECORD 鎖,0x000000000202 表示其對應的6字節的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字節的rowid來作為聚集索引,事務A造成的鎖會對 輔助索引xid=3 記錄對應的聚集索引也加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對應的聚集索引添加X鎖,所以輔助索引xid=7這條記錄并沒有上X鎖,實際上只是用來表示與前一行記錄在 (3,7)區間構成間隙鎖。
事務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 | 事務B在事務A已經持有鎖的情況下,進行插入(id,xid) ====>(1,2) 發現(1,3)存在間隙鎖。無法進行插入操作。
Next-Key Lock 是結合了Gap Lock和Record Lock的合并,其設計目的主要是為解決RR級別下的幻讀問題。該鎖定方式相對于Gap Lock和Record Lock是帶閉合區間的范圍鎖定。
以下介紹其特點:
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隔離級別下對一張只有主鍵的表進行操作
主鍵只是由一列構成
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)
事務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 | 由于我們顯示的定義了主鍵可以看到這里LOCK_DATA=4就是這個表內建的rowid ,說明聚集索引上的id=4的行記錄直接被加了X鎖,也并沒有用到GAP鎖。
該場景與GAP鎖的場景相比較可以發現,在RR隔離級別下當過濾條件的列是主鍵或者唯一索引的情況下,因為該列值都是唯一值,innodb存儲引擎會對Next-Key Lock進行優化,Next-Key Lock會降級成為Record Lock。換句話說gap鎖只存在于RR隔離級別下的輔助索引中,主鍵和唯一索引由于本身具有唯一約束,不需要gap鎖,只有record lock
如果主鍵由多列構成,但是只使用其中的一列進行查詢呢?
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)
我們看到了什么?可以發現當主鍵由多列構成時,我們只使用主鍵列中的一列進行查詢時,依然使用到了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)
事務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 |
事務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 | 由于事務A在輔助索引記錄 (3,7)之間是間隙鎖,而事務B插入的數據(id,xid) ===>(5,5) 中的xid=5在(3,7) 間隙鎖范圍中,所以才會顯示LOCK_MODE =X,GAP LOCK_STATUS=WAITING LOCK_DATA = 7, 0x000000000203 會等待事務A將鎖釋放直至超時。
事務C說明事務A造成的間隙鎖實際上并沒有將輔助索引記錄xid=7也鎖住,不包含記錄xid=7。
事務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)
事務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字節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)
事務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表為例。
我們可以得出以下結論:
在RR隔離級別下,如果表中只有一個非唯一的輔助索引,當進行等值當前讀時會與下一行記錄形成間隙鎖,但不會鎖住下一行記錄;范圍當前讀時會與下一行記錄形成間隙鎖并且會鎖住該行。
那么可能有人會問在RR隔離級別下,為什么在對輔助索引做范圍當前讀時會與下一行記錄形成間隙鎖并且還會鎖住該行呢?
因為對于非唯一的輔助索引不能保證鍵值的唯一,所以需要鎖住滿足條件的下一行進行判斷,當然這里還有查詢優化器的功勞。
場景三: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 | t2 | NULL | NULL | NULL | 139846618228824 | TABLE | IX | GRANTED | NULL | | INNODB | 2065:4:5:4 | 2065 | 64 | 43 | test | t2 | NULL | NULL | xid | 139846618225784 | RECORD | X | GRANTED | 3, 4 | | INNODB | 2065:4:4:4 | 2065 | 64 | 43 | test | t2 | NULL | NULL | PRIMARY | 139846618226128 | RECORD | X | GRANTED | 4 | | INNODB | 2065:4:5:5 | 2065 | 64 | 43 | test | t2 | NULL | NULL | xid | 139846618226472 | RECORD | X,GAP | GRANTED | 7, 7 | +--------+----------------+-----------------------+-----------+----------+---------------+-------------+----------------+-------------------+------------+-----------------------+-----------+-----------+-------------+-----------+ 10 rows in set (0.01 sec)
事務A造成的鎖下圖所示:
| INNODB | 2065:1061 | 2065 | 64 | 43 | test | t2 | NULL | NULL | NULL | 139846618228824 | TABLE | IX | GRANTED | NULL | | INNODB | 2065:4:5:4 | 2065 | 64 | 43 | test | t2 | NULL | NULL | xid | 139846618225784 | RECORD | X | GRANTED | 3, 4 | | INNODB | 2065:4:4:4 | 2065 | 64 | 43 | test | t2 | NULL | NULL | PRIMARY | 139846618226128 | RECORD | X | GRANTED | 4 | | INNODB | 2065:4:5:5 | 2065 | 64 | 43 | test | t2 | NULL | NULL | xid | 139846618226472 | RECORD | X,GAP | GRANTED | 7, 7 |
分析:在RR隔離級別下,一張帶有主鍵和非唯一的輔助索引表,使用輔助索引進行當前讀時與只有非唯一的輔助索引表進行等值當前讀時,都不會將輔助索引的下一行記錄進行加鎖,只會將下一行記錄與前一行記錄添加間隙鎖。
范圍當前讀
事務A的造成的鎖:
root@localhost : test 11:07:47> 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 | 2086:1061 | 2086 | 64 | 52 | test | t2 | NULL | NULL | NULL | 139846618228824 | TABLE | IX | GRANTED | NULL | | INNODB | 2086:4:5:4 | 2086 | 64 | 52 | test | t2 | NULL | NULL | xid | 139846618225784 | RECORD | X | GRANTED | 3, 4 | | INNODB | 2086:4:5:5 | 2086 | 64 | 52 | test | t2 | NULL | NULL | xid | 139846618225784 | RECORD | X | GRANTED | 7, 7 | | INNODB | 2086:4:5:6 | 2086 | 64 | 52 | test | t2 | NULL | NULL | xid | 139846618225784 | RECORD | X | GRANTED | 9, 10 | | INNODB | 2086:4:4:4 | 2086 | 64 | 52 | test | t2 | NULL | NULL | PRIMARY | 139846618226128 | RECORD | X | GRANTED | 4 | | INNODB | 2086:4:4:5 | 2086 | 64 | 52 | test | t2 | NULL | NULL | PRIMARY | 139846618226128 | RECORD | X | GRANTED | 7 | | INNODB | 2086:4:4:6 | 2086 | 64 | 52 | test | t2 | NULL | NULL | PRIMARY | 139846618226128 | RECORD | X | GRANTED | 10 | +--------+----------------+-----------------------+-----------+----------+---------------+-------------+----------------+-------------------+------------+-----------------------+-----------+-----------+-------------+-----------+ 7 rows in set (0.00 sec)
綜上所述,在RR隔離級別下的加鎖方式:
說明:對于唯一鍵值的鎖定,next_key lock 降級為 Record lock 僅存在于查詢所有的唯一索引列。如果唯一索引有多個列組成,而查詢僅是多個唯一索引列中的一個,那么查詢其實是rang類型的查詢,而不是point類型的查詢,故innodb存儲引擎依然使用next_key lock進行鎖定。
上述內容就是innodb中怎么實現一個存儲引擎鎖,你們學到知識或技能了嗎?如果還想學到更多技能或者豐富自己的知識儲備,歡迎關注億速云行業資訊頻道。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。