0
点赞
收藏
分享

微信扫一扫

Python进阶篇:MySQL锁详解(篇幅较长,请耐心)



目录

  • ​​1,什么是锁?​​
  • ​​2,InnoDB中的锁​​
  • ​​3,锁的颗粒度与效率​​
  • ​​4,共享锁与排他锁​​
  • ​​4.1 共享锁​​
  • ​​4.2 排它锁​​
  • ​​4.3 意向共享锁(IS)/排它锁(IX)​​
  • ​​5,记录锁,间隙锁与临键锁​​
  • ​​5.1 锁到底锁住什么?​​
  • ​​锁住了什么之行验证​​
  • ​​锁住了什么之列验证​​
  • ​​5.2 记录锁(Record Locks)​​
  • ​​5.3 间隙锁(Gap Locks)​​
  • ​​5.4 临键锁(Next-key Locks)​​
  • ​​6,总结​​

1,什么是锁?

锁是计算机解决并发情况下多线程/进程造成数据不一致问题的一种机制。通过锁来保证数据的一致性。但是锁也很大程度影响了数据库并发的效率。

2,InnoDB中的锁

以下是innodb中的锁:

  • ​​Shared and Exclusive Locks​​
  • ​​Intention Locks​​
  • ​​Record Locks​​
  • ​​Gap Locks​​
  • ​​Next-Key Locks​​
  • ​​Insert Intention Locks​​
  • ​​AUTO-INC Locks​​
  • ​​Predicate Locks for Spatial Indexes​​

本文主要介绍共享与排他锁(Shared and Exclusive Locks),意向锁(Intention locks),间隙锁(Gap Locks),记录锁(Record Locks),临键锁(Next-Key Locks)。

3,锁的颗粒度与效率

行锁: 锁住一行记录的锁

表锁: 锁住一张表的锁

效率:表>行 直接锁住整张表,干脆直接

粒度:表>行 表锁住的数据资源更大

冲突:表>行 表锁住的资源更大,冲突几率更大

性能:表<行 冲突几率越小,并发性能更高

4,共享锁与排他锁

4.1 共享锁

共享锁也叫S锁,读锁。多个事务能共享读取同一数据,但是在有事务获取到共享锁时,其它事务不能修改,从而保证了数据的一致性。 加锁的方式为在查询语句后加Lock in share mode;

关闭事务自动提交,开启双窗口模拟两个事务,并进行查询。

Python进阶篇:MySQL锁详解(篇幅较长,请耐心)_后端

事务1加锁查询:

Python进阶篇:MySQL锁详解(篇幅较长,请耐心)_后端_02

事务2查询:

Python进阶篇:MySQL锁详解(篇幅较长,请耐心)_经验分享_03

事务2修改:

Python进阶篇:MySQL锁详解(篇幅较长,请耐心)_mysql_04

4.2 排它锁

排它锁,也叫X锁,写锁。有事务获取了某行排它锁后,其它事务读写都将被排斥。 排他锁加锁的方式分为手动加锁和自动加锁,手动加锁的方式是在查询语句后加for update;自动加锁表示delete/update/insert语句会自动加X锁。

事务1加锁:

Python进阶篇:MySQL锁详解(篇幅较长,请耐心)_python_05

事务2操作:

Python进阶篇:MySQL锁详解(篇幅较长,请耐心)_mysql_06


4.3 意向共享锁(IS)/排它锁(IX)

意向锁是表锁,由InnoDB自己维护,用户无法操作。

在假设的事务可以获得对某假定行的S 锁定之前,它必须首先获得对包含该行的表的一个IS 或者更强的锁定。

在假设的事务可以获得对某假定行的X 锁定之前,它必须首先获得对包含该行的表的一个IX 锁定。

下图是锁兼容矩阵图:

Python进阶篇:MySQL锁详解(篇幅较长,请耐心)_后端_07

5,记录锁,间隙锁与临键锁

5.1 锁到底锁住什么?

锁到底锁住了什么?是行还是列?接下来通过一系列操作来验证锁到底锁住了什么。

锁住了什么之行验证

事务1给id为1的行上锁:

Python进阶篇:MySQL锁详解(篇幅较长,请耐心)_python_08

事务2操作id为2的行:

Python进阶篇:MySQL锁详解(篇幅较长,请耐心)_python_09

结论:锁住的不是行,那是否是列呢?

锁住了什么之列验证

事务1 操作列name:

Python进阶篇:MySQL锁详解(篇幅较长,请耐心)_数据库_10

事务2操作列id:

Python进阶篇:MySQL锁详解(篇幅较长,请耐心)_mysql_11

结论:锁锁住的也不是列。

那到底锁住了什么呢?是索引。InnoDB引擎下的所有表都有索引,如果你不创建任何索引,那么它会选择隐藏的RowID作为聚集索引。有兴趣的同学可以去验证一下。接下来要谈到的就是索引中的三种锁算法。

​ A record lock is a lock on an index record. For example, ​​SELECT c1 FROM t WHERE c1 = 10 FOR UPDATE;​​​ prevents any other transaction from inserting, updating, or deleting rows where the value of ​​t.c1​​​ is ​​10​​.

Record locks always lock index records, even if a table is defined with no indexes. For such cases, ​​InnoDB​​ creates a hidden clustered index and uses this index for record locking.

5.2 记录锁(Record Locks)

为了更好的演示,修改了user表,并且配出主键间隙图。

Python进阶篇:MySQL锁详解(篇幅较长,请耐心)_mysql_12

主键间隙图:

Python进阶篇:MySQL锁详解(篇幅较长,请耐心)_数据库_13

记录锁:在使用主键索引或者唯一索引查询时自动使用,锁住的就是where条件指定的记录行,此时该记录只能被查询,不能被修改。

事务1查询id=1:

Python进阶篇:MySQL锁详解(篇幅较长,请耐心)_mysql_14

事务2查询id=1:

Python进阶篇:MySQL锁详解(篇幅较长,请耐心)_python_15

5.3 间隙锁(Gap Locks)

间隙锁:在操作id1-4之间空隙记录时,会在1-4加上一个间隙锁,主要的目的是为了防止其它事务在该间隙更改了数据,从而影响了数据的一致性。

事务1查询不存在的记录2:

Python进阶篇:MySQL锁详解(篇幅较长,请耐心)_mysql_16

事务2插入该间隙数据:

Python进阶篇:MySQL锁详解(篇幅较长,请耐心)_数据库_17

5.4 临键锁(Next-key Locks)

上述记录锁在使用范围查询时则不行,而间隙锁在>=/<=这种条件下也会失效。所以将记录锁与间隙锁的锁定范围合并,就成了临建锁,临建锁在条件为范围查询时生效,临建锁是左开右闭的形式。并且在查询条件变更时会自动变成对应条件的记录锁或间隙锁。

事务1开启范围查询:

此时会锁住到10这个主键,另外事务查询10会被阻塞

Python进阶篇:MySQL锁详解(篇幅较长,请耐心)_mysql_18

事务2查询10:

Python进阶篇:MySQL锁详解(篇幅较长,请耐心)_数据库_19


6,总结

通过这几种锁的学习,可以明白MySQL是如何在可重复读隔离级别下是如何去解决幻读问题来保证读一致性。还有一些点就不一一详述了,


举报

相关推荐

0 条评论