日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 运维知识 > 数据库 >内容正文

数据库

浅谈MySQL的七种锁

發布時間:2023/12/20 数据库 32 豆豆
生活随笔 收集整理的這篇文章主要介紹了 浅谈MySQL的七种锁 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

一、共享鎖(S鎖)/排他鎖(X鎖)

????事務拿到某一行記錄的共享S鎖,才可以讀取這一行,并阻止別的事物對其添加X鎖
????事務拿到某一行記錄的排它X鎖,才可以修改或者刪除這一行
????共享鎖的目的是提高讀讀并發
????排他鎖的目的是為了保證數據的一致性

二、意向鎖

1、意向共享鎖
????預示事務有意向對表中的某些行加共享S鎖

2、意向排他鎖
????預示著事務有意向對表中的某些行加排他X鎖

3、 IS、S、IX、X鎖之間的兼容性比較:

兼容性ISIXSX
IS兼容兼容兼容互斥
IX兼容兼容互斥互斥
S兼容互斥兼容互斥
X互斥互斥互斥互斥

4、意向鎖的意義在哪里?

????1.IX,IS是表級鎖,不會和行級的X,S鎖發生沖突。只會和表級的X,S發生沖突
????2.意向鎖是在添加行鎖之前添加。
????3.如果沒有意向鎖,當向一個表添加表級X鎖時,就需要遍歷整張表來判斷是否存行鎖,以免發生沖突
????4.如果有了意向鎖,只需要判斷該意向鎖與表級鎖是否兼容即可。

三、插入意向鎖(insert intention looks)

????插入意向鎖是間隙鎖的一種,針對insert操作產生。
????目的是提高插入并發。
????多個事物,在同一個索引,同一個范圍區間進行插入記錄的時候,如果 插入的位置不沖突,不會阻塞彼此。

示例:

t1(id primary key,id1 int)mysql> select * from t1; +----+------+ | id | id1 | +----+------+ | 10 | 10 | | 20 | 20 | | 30 | 30 | +----+------+ 3 rows in set (0.00 sec)mysql> start transaction; mysql> start transaction; Query OK, 0 rows affected (0.00 sec) Query OK, 0 rows affected (0.00 sec) mysql> insert into t1 values(11,11); mysql> insert into t1 values(12,12); Query OK, 1 row affected (0.00 sec) Query OK, 1 row affected (0.00 sec) mysql> select * from t1; mysql> select * from t1; +----+------+ +----+------+ | id | id1 | | id | id1 | +----+------+ +----+------+ | 10 | 10 | | 10 | 10 | | 11 | 11 | | 12 | 12 | | 20 | 20 | | 20 | 20 | | 30 | 30 | | 30 | 30 | +----+------+ +----+------+ 4 rows in set (0.00 sec) 4 rows in set (0.00 sec)

????由于事物一和事物二都是對表的同一索引范圍進行insert,使用的插入意向鎖,由于插入的記錄并不沖突,所以并不會阻塞事物二。如果事物二插入的記錄與事物一沖突,會被X鎖阻塞。

四、記錄鎖

????對單條索引記錄進行加鎖,鎖住的是索引記錄而非記錄本身,即使表中沒有任何索引,MySQL會自動創建一個隱式的row_id作為聚集索引來進行加鎖。

t1(id int primary key,id1 int)mysql> start transaction; Query OK, 0 rows affected (0.00 sec)mysql> select * from t1 where id=20 for update; //記錄鎖,鎖住id=20 +----+------+ | id | id1 | +----+------+ | 20 | 20 | +----+------+ 1 row in set (0.00 sec)mysql> start transaction; Query OK, 0 rows affected (0.00 sec) mysql> update t1 set id1=200 where id=20; //被hang住

五、間隙鎖(gap鎖)

????封鎖記錄中的間隔,防止間隔中被其他事務插入。
????間隙鎖主要出現在RR隔離級別,避免出現幻讀。

1、MVCC(多版本并發)

????1.MVCC的作用

????????避免臟讀、寫不阻塞讀、實現可重復讀、多版本控制

????2.在MVCC下,讀操作可以分為兩種:快照讀、當前讀

????????1)快照讀

????????select * from tbl_name where ...

????????2)當前讀
????????select * from tbl_name where ... for update;
????????update
????????delete
????????insert

????????3)為什么delete/update也是一種當前讀?(如一個update操作)

???????????a.在進行update的時候,MySQL會根據where條件得到過濾出來的第一條記錄,并進行加鎖(currenet read)
???????????b.對該條記錄進行update
???????????c.再次讀取下一條記錄,直到沒有滿足條件的記錄為止
???????????d.delete原理與之類似

????????4)為什么insert也是一種當前讀?
????????????insert操作可能會觸發Unique Key的沖突檢查,也會進行一個當前讀。

2、隔離級別

????1.Read Uncommitted

????????可以讀取到未提交的事物

????2.Rrad Committed(RC)

????????針對當前讀,RC隔離級別保證對讀取到的記錄加鎖 (記錄鎖),存在幻讀現象。

????3.Repeatable Read (RR)

????????針對當前讀,RR隔離級別保證對讀取到的記錄加鎖 (記錄鎖),同時保證對讀取的范圍加鎖,新的滿足查詢條件的記錄不能夠插入 (間隙鎖),不存在幻讀現象。

????4.Serializable

????????所有的讀操作均為當前讀,讀加讀鎖 (S鎖),寫加寫鎖 (X鎖)。
????????讀寫沖突,并發行很差

3、幾種觸發間隙鎖的情況

1.id非唯一索引+RR

????????SQL:delete from t1 where id = 10;

????加鎖流程如下:

a.通過id索引定位到第一條滿足查詢條件的記錄,加記錄上的X鎖,加GAP上的GAP鎖,b.然后加主鍵聚簇索引上的記錄X鎖,然后返回;c.然后讀取下一條,重復進行。d.直至進行到第一條不滿足條件的記錄[11,f],此時,不需要加記錄X鎖,但是仍舊需要加GAP鎖,最后返回結束。

?

2.id無索引+RR

????????SQL:delete from t1 where id = 10;

????加鎖流程如下:

a.由于id字段無索引,進行全表掃描的當前讀,b.聚簇索引上的所有記錄,都被加上了X鎖。其次,聚簇索引每條記錄間的間隙都被加上了GAP鎖。

?

3.針對id無索引+RR MySQL性能上做的一些優化

????semi-consistent read
????semi-consistent read開啟的情況下,對于不滿足查詢條件的記錄,MySQL會提前放鎖。
????針對上面的這個用例,就是除了記錄[d,10],[g,10]之外,所有的記錄鎖都會被釋放,同時不加GAP鎖。

4.semi-consistent read如何觸發?

????????1)隔離級別是read committed;
????????2)隔離級別是Repeatable Read,同時設置了 innodb_locks_unsafe_for_binlog 參數。

示例一:非唯一索引 + 等值當前讀

mysql> start transaction; Query OK, 0 rows affected (0.00 sec)mysql> delete from t2 where myid = 100; Query OK, 2 rows affected (0.00 sec) mysql> start transaction; Query OK, 0 rows affected (0.00 sec) mysql> insert into t2 values(3,98); ^C^C -- query aborted ERROR 1317 (70100): Query execution was interrupted mysql> insert into t2 values(134,98); ^C^C -- query aborted ERROR 1317 (70100): Query execution was interrupted mysql> insert into t2 values(7,104); ERROR 1062 (23000): Duplicate entry '7' for key 'PRIMARY'mysql> insert into t2 values(8,104); ^C^C -- query aborted ERROR 1317 (70100): Query execution was interrupted mysql> insert into t2 values(118,104); ^C^C -- query aborted ERROR 1317 (70100): Query execution was interrupted mysql> insert into t2 values(118,105); Query OK, 1 row affected (0.00 sec)

?
示例二:非唯一索引 + 范圍當前讀

mysql> start transaction; Query OK, 0 rows affected (0.00 sec)mysql> select * from t2 where myid > 100 for update; +-----+------+ | id | myid | +-----+------+ | 98 | 105 | | 123 | 109 | +-----+------+ 2 rows in set (0.00 sec)mysql> start transaction; Query OK, 0 rows affected (0.00 sec) mysql> insert into t2 values(8,100); ^C^C -- query aborted ERROR 1317 (70100): Query execution was interruptedmysql> insert into t2 values(4,100); Query OK, 1 row affected (0.00 sec) mysql> insert into t2 values(3,101); ^C^C -- query aborted ERROR 1317 (70100): Query execution was interruptedmysql> insert into t2 values(99,101); ^C^C -- query aborted ERROR 1317 (70100): Query execution was interruptedmysql> insert into t2 values(99,108); ^C^C -- query aborted ERROR 1317 (70100): Query execution was interruptedmysql> insert into t2 values(134,128); ^C^C -- query aborted ERROR 1317 (70100): Query execution was interrupted

?
示例三:主鍵索引 + 范圍當前讀

mysql> select * from t2 where id > 100 for update; +-----+------+ | id | myid | +-----+------+ | 123 | 109 | | 999 | 56 | +-----+------+ 2 rows in set (0.00 sec)mysql> start transaction; Query OK, 0 rows affected (0.00 sec) mysql> insert into t2 values(99,192); ^C^C -- query aborted ERROR 1317 (70100): Query execution was interrupted mysql> insert into t2 values(125,192); ^C^C -- query aborted ERROR 1317 (70100): Query execution was interrupted mysql> insert into t2 values(1259,192); ^C^C -- query aborted ERROR 1317 (70100): Query execution was interrupted mysql> select * from t2 where id=123 lock in share mode;^C^C -- query aborted ERROR 1317 (70100): Query execution was interrupted mysql> select * from t2 where id=124 lock in share mode;Empty set (0.00 sec)

六、臨鍵鎖(Next-Key Locks)

????臨鍵鎖是記錄鎖和間隙鎖的組合,既鎖住了記錄也鎖住了范圍。

????臨鍵鎖的主要目的,也是為了避免幻讀。

????如果把事務的隔離級別降級為RC,臨鍵鎖就也會失效。

????通常情況下,InnoDB在搜索或掃描索引的行鎖機制中使用“臨鍵鎖(next-key locking)”算法來鎖定某索引記錄及其前部的間隙(gap),以阻塞其它用戶緊跟在該索引記錄之前插入其它索引記錄。

????innodb_locks_unsafe_for_binlog默認為OFF,意為禁止使用非安全鎖,也即啟用間隙鎖功能。將其設定為ON表示禁止鎖定索引記錄前的間隙,也即禁用間隙鎖,InnoDB僅使用索引記錄鎖(index-record lock)進行索引搜索或掃描,不過,這并不禁止InnoDB在執行外鍵約束檢查或重復鍵檢查時使用間隙鎖。

????innodb_locks_unsafe_for_binlog的效果:

????(1)對UPDATE或DELETE語句來說,InnoDB僅鎖定需要更新或刪除的行,對不能夠被WHERE條件匹配的行施加的鎖會在條件檢查后予以釋放。這可以有效地降低死鎖出現的概率;
????(2)執行UPDATE語句時,如果某行已經被其它語句鎖定,InnoDB會啟動一個“半一致性(semi-consistent)”讀操作從MySQL最近一次提交版本中獲得此行,并以之判定其是否能夠并當前UPDATE的WHERE條件所匹配。如果能夠匹配,MySQL會再次對其進行鎖定,而如果仍有其它鎖存在,則需要先等待它們退出。
????(3)innodb_locks_unsafe_for_binlog可能會造成幻讀

?
示例一:innodb_locks_unsafe_for_binlog=off的情況下:

mysql> show create table t4\G *************************** 1. row ***************************Table: t4 Create Table: CREATE TABLE `t4` (`id` int(11) NOT NULL,`id1` int(11) DEFAULT NULL,`id2` int(11) DEFAULT NULL,PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=latin1 1 row in set (0.00 sec)mysql> show create table t5\G *************************** 1. row ***************************Table: t5 Create Table: CREATE TABLE `t5` (`id` int(11) NOT NULL,`id1` int(11) DEFAULT NULL,`id2` int(11) DEFAULT NULL,PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=latin1 1 row in set (0.00 sec)mysql> show variables like 'innodb_locks_unsafe_for_binlog'; +--------------------------------+-------+ | Variable_name | Value | +--------------------------------+-------+ | innodb_locks_unsafe_for_binlog | OFF | +--------------------------------+-------+ 1 row in set (0.00 sec)mysql> start transaction; Query OK, 0 rows affected (0.00 sec)mysql> select * from t4; +----+------+------+ | id | id1 | id2 | +----+------+------+ | 1 | 1 | 1 | | 2 | 2 | 2 | | 3 | 3 | 3 | | 4 | 4 | 4 | | 5 | 5 | 5 | +----+------+------+ 5 rows in set (0.00 sec)mysql> select * from t5; Empty set (0.00 sec)mysql> insert into t5 select * from t4 where id2=3; Query OK, 1 row affected (0.34 sec) Records: 1 Duplicates: 0 Warnings: 0mysql> start transaction; Query OK, 0 rows affected (0.00 sec) mysql> update t4 set id1=33 where id2=3; //被hang住mysql> commit; Query OK, 0 rows affected (0.01 sec)Query OK, 1 row affected (9.15 sec) //事物一提交后,update操作執行成功 Rows matched: 1 Changed: 1 Warnings: 0mysql> select * from t5; +----+------+------+ | id | id1 | id2 | +----+------+------+ | 3 | 3 | 3 | +----+------+------+ 1 row in set (0.00 sec)mysql> select * from t4 where id2=3; +----+------+------+ | id | id1 | id2 | +----+------+------+ | 3 | 3 | 3 | +----+------+------+ 1 row in set (0.00 sec)

?
示例二:innodb_locks_unsafe_for_binlog=on的情況下

mysql> show variables like 'innodb_locks_unsafe_for_binlog'; +--------------------------------+-------+ | Variable_name | Value | +--------------------------------+-------+ | innodb_locks_unsafe_for_binlog | ON | +--------------------------------+-------+ 1 row in set (0.35 sec)mysql> select * from t4; +----+------+------+ | id | id1 | id2 | +----+------+------+ | 1 | 1 | 1 | | 2 | 2 | 2 | | 3 | 3 | 3 | | 4 | 4 | 4 | | 5 | 5 | 5 | +----+------+------+ 5 rows in set (0.00 sec)mysql> select * from t5; Empty set (0.00 sec)mysql> start transaction; Query OK, 0 rows affected (0.00 sec)mysql> insert into t5 select * from t4 where id2=3; Query OK, 1 row affected (0.00 sec) Records: 1 Duplicates: 0 Warnings: 0mysql> select * from t5; +----+------+------+ | id | id1 | id2 | +----+------+------+ | 3 | 3 | 3 | +----+------+------+ 1 row in set (0.00 sec)mysql> start transaction; Query OK, 0 rows affected (0.00 sec) mysql> update t4 set id2=333 where id2=3; //事物一未提交的情況下,直接更新成功,不會有阻塞Query OK, 1 row affected (0.00 sec) Rows matched: 1 Changed: 1 Warnings: 0 mysql> select * from t4 where id=3; +----+------+------+ | id | id1 | id2 | +----+------+------+ | 3 | 3 | 333 | +----+------+------+ 1 row in set (0.00 sec) mysql> select * from t5; Empty set (0.00 sec) mysql> commit; //事物二先提交 Query OK, 0 rows affected (0.00 sec) mysql> commit; //事物一后提交 Query OK, 0 rows affected (0.01 sec) mysql> select * from t5; +----+------+------+ | id | id1 | id2 | +----+------+------+ | 3 | 3 | 3 | +----+------+------+ 1 row in set (0.00 sec) mysql> select * from t4 where id=3; +----+------+------+ | id | id1 | id2 | +----+------+------+ | 3 | 3 | 333 | +----+------+------+ 1 row in set (0.00 sec)

?
查看binlog日志:

BEGIN /*!*/; # at 565 #180926 16:27:06 server id 1013306 end_log_pos 609 Table_map: `test1`.`t4` mapped to number 125 # at 609 #180926 16:27:06 server id 1013306 end_log_pos 667 Update_rows: table id 125 flags: STMT_END_FBINLOG ' muurWxM6dg8ALAAAAGECAAAAAH0AAAAAAAEABXRlc3QxAAJ0NAADAwMDAAY= muurWx86dg8AOgAAAJsCAAAAAH0AAAAAAAEAAgAD///4AwAAAAMAAAADAAAA+AMAAAADAAAATQEA AA== '/*!*/; ### UPDATE `test1`.`t4` //事物二先提交,所以binlog日志中先記錄對t4的更新操作 ### WHERE ### @1=3 /* INT meta=0 nullable=0 is_null=0 */ ### @2=3 /* INT meta=0 nullable=1 is_null=0 */ ### @3=3 /* INT meta=0 nullable=1 is_null=0 */ ### SET ### @1=3 /* INT meta=0 nullable=0 is_null=0 */ ### @2=3 /* INT meta=0 nullable=1 is_null=0 */ ### @3=333 /* INT meta=0 nullable=1 is_null=0 */ # at 667 #180926 16:28:38 server id 1013306 end_log_pos 694 Xid = 82 COMMIT/*!*/; # at 694 #180926 16:28:52 server id 1013306 end_log_pos 755 GTID last_committed=1 sequence_number=3 rbr_only=yes /*!50718 SET TRANSACTION ISOLATION LEVEL READ COMMITTED*//*!*/; SET @@SESSION.GTID_NEXT= 'f2754eef-6a6e-11e8-8b99-000c2971d3ea:1451'/*!*/; # at 755 #180926 16:25:57 server id 1013306 end_log_pos 824 Query thread_id=2 exec_time=0 error_code=0 SET TIMESTAMP=1537993557/*!*/; BEGIN /*!*/; # at 824 #180926 16:25:57 server id 1013306 end_log_pos 868 Table_map: `test1`.`t5` mapped to number 126 # at 868 #180926 16:25:57 server id 1013306 end_log_pos 912 Write_rows: table id 126 flags: STMT_END_FBINLOG ' VeurWxM6dg8ALAAAAGQDAAAAAH4AAAAAAAEABXRlc3QxAAJ0NQADAwMDAAY= VeurWx46dg8ALAAAAJADAAAAAH4AAAAAAAEAAgAD//gDAAAAAwAAAAMAAAA= '/*!*/; ### INSERT INTO `test1`.`t5` //事物一后提交所以對于insert ... select 操作在binlog中后記錄,但是set的記錄仍然是事物二為修改之前的值 ### SET ### @1=3 /* INT meta=0 nullable=0 is_null=0 */ ### @2=3 /* INT meta=0 nullable=1 is_null=0 */ ### @3=3 /* INT meta=0 nullable=1 is_null=0 */ # at 912 #180926 16:28:52 server id 1013306 end_log_pos 939 Xid = 78 COMMIT/*!*/; SET @@SESSION.GTID_NEXT= 'AUTOMATIC' /* added by mysqlbinlog */ /*!*/; DELIMITER ;

?
????因此,當innodb_locks_unsafe_for_binlog=on的情況下,會讓你容易造成數據的不一致。

七、自增長鎖

????自增長鎖是一種表級鎖,專門針對auto_increment類型的列。

????自增長列鎖各模式分析:

????innodb_autoinc_lock_mode:自增長長鎖模式

0:不管是insert into values (simple insert)還是insert into select (bulk insert),都是:持有鎖、讀取/修改、執行SQL、釋放,不需要等到事務提交就釋放鎖,但是需要SQL執行完成,并且不能保證連續。持有latch ---> 讀取和修改auto鎖 ---> 執行insert ---> 釋放注意:不需要等待insert所在的事務是否提交缺點:可能出現數字不連續持有時間相對過長:SQL執行完畢,不需要事務提交1:默認值,對于回滾是不能保證自增長列連續的。對于simple insert (insert into values):持有鎖、讀取、釋放、執行SQL,最快,不需要執行完SQL就釋放,不需要等待insert執行完畢就可以釋放鎖。對于bulk insert (insert into select):持有鎖、讀取、執行SQL、釋放,需要執行完SQL才釋放。(對于批量insert來說等同于0)優點:對于simple insert 來說,性能比0好些,對于批量來說,性能等同于0缺點:數字不連續對于批量來說持有鎖的時間相對過長2:經常改為2,主要是為了唯一,不是為了連續,在批量insert時或者批量insert并發的時候用優點:速度最快缺點:只能保證唯一,不能保證遞增和連續。持有、讀取和修改、釋放、執行SQL建議修改成2,對于批量的insert可以提升性能

示例:
1、自增長鎖

t2(id automent_ment,id1 int)mysql> show variables like 'innodb_autoinc_lock_mode'; +--------------------------+-------+ | Variable_name | Value | +--------------------------+-------+ | innodb_autoinc_lock_mode | 1 | +--------------------------+-------+ 1 row in set (0.04 sec)mysql> start transaction; Query OK, 0 rows affected (0.00 sec)mysql> insert into t2(id1) values(11); Query OK, 1 row affected (0.00 sec)mysql> select * from t2; +----+------+ | id | id1 | +----+------+ | 1 | 11 | +----+------+ 1 row in set (0.00 sec) mysql> start transaction; Query OK, 0 rows affected (0.00 sec) mysql> insert into t2(id1) values(22);Query OK, 1 row affected (0.03 sec) mysql> insert into t2(id1) values(33); Query OK, 1 row affected (0.00 sec) mysql> select * from t2; +----+------+ | id | id1 | +----+------+ | 1 | 11 | | 3 | 33 | +----+------+ 2 rows in set (0.00 sec) mysql> select * from t2;+----+------+ | id | id1 | +----+------+ | 2 | 22 | +----+------+ 1 row in set (0.00 sec)

????由于innodb_autoinc_lock_mode=1,所以事物一并不會阻塞事物二的simple insert,保證了id字段的唯一性

參考引用:
何登成的技術博客/MySQL 加鎖處理分析:http://hedengcheng.com/?p=771
微信公眾號/架構師之路:https://mp.weixin.qq.com/mp/profile_ext?action=home&__biz=MjM5ODYxMDA5OQ==&scene=124wechat_redirect

總結

以上是生活随笔為你收集整理的浅谈MySQL的七种锁的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。