浅谈数据库的锁(一)

全局锁和表锁 — server层

全局锁

​ 顾名思义,全局锁就是给整个数据库实例进行加锁。命令是Flush tables with read lock

​ 当需要整个数据库进入只读状态时,就用这个命令。一般来说,只有全局备份才会使用这个命令,但即使是做全局备份,我也不推荐使用这个锁。

​ 因为对全局加锁,带来的坏处很多很多。

  • 对全局进行加锁,那么基本上所有操作都会被阻塞,通常情况业务就会停摆。

  • 如果在从库进行备份,主库传来的binlog,从库不能执行。这会加大主从数据库之间的延迟

  • 通过Read - View来进行全局备份,效率更高。

保留全局锁的原因

​ 为了让不支持事务的存储引擎也能够保持一致性。全局锁,每次只会让一个“事务”操作数据库。

表级锁

MySQL里面表级别的锁有两种:

  • 一种是表锁
  • 另一种是元数据锁 (meta data lock,MDL)。
表锁 — DML

表锁,又分为 read lock 和 write lock。表级读写锁有以下特点:

  • 很显然跟其他读写锁一样,都是符合读读共享,读写和写写是互斥的

  • 除此以外,还有另外 表锁不仅限制别的事务的操作,还有限制当前持有锁的事务的操作

1
2
3
特别值得注意的有以下两点,其他都跟别读写规则一样
1.持有读锁的事务A 会阻塞想要修改该对象的事务B
2.持有读锁的事务A,接下来事务A也只能对该对象进行读操作,写操作也是不允许的。
MDL锁 — DDL

​ 为什么要有MDL锁?想象一下这种场景,事务A在对表1进行查询(查询到一半),此时另外一个事务B要对表进行结构上的增加字段。

​ 那么,我们希望查询出来的结果,一半是旧的表结构,另一半是新的表结构,多一些字段吗?显然不。

​ 因此,就需要额外的措施去进行一下限制。但是这依靠表读写锁是做不到的。因为表读写锁,仅仅限制的是DML操作,对DDL操作没有做任何限制。

​ 因此,需要另外一种锁去限制表的DDL操作,也就是MDL锁

MDL如何起作用?

其实它的作用原理,跟读写锁非常相似:

  • 当事务对表的数据做 DML操作时,事务对该表加 MDL读锁。

    • 因为读锁之间共享,所以不影响别的事务对表的MDL操作
  • 当事务要对表做DDL操作时,事务对表加MDL写锁。

    • 因为读写之间互斥,只要有事务在DML,那么表结构修改就会被阻塞
    • 同理,只要DDL还没被提交,那么别的事务对表的DML就会被阻塞
    • 这样保证了,查询出来的数据,结构都是一样的
给表加字段的坑

有时候给表加一个字段,导致整个库挂了。

1633185917151

  • sessionA先启动,拿到MDL读锁
  • sessionB启动拿到读锁,并执行
  • sessionC需要修改表结构,申请写锁,被阻塞。
  • sessionD想要申请读锁,但是被阻塞住了
    • 个人猜测,申请锁应该是通过一个队列,类似对头阻塞的机制

​ 因此,sessionC被阻塞其实没什么关系,但是会导致sessionC后面的所有查询事务都被阻塞了。最主要的原因就是,事务中某个语句申请一把锁。该锁是会等整个事务结束才把锁释放。

行锁 — 各自引擎具体实现

 MySQL 的行锁是在引擎层由各个引擎自己实现的。但并不是所有的引擎都支持行锁,比如 MyISAM 引擎就不支持行锁。

​ 不支持行锁意味着并发控制只能使用表锁,对于这种引擎的表,同一张表上任何时刻只能有一个更新在执行,这就会影响到业务并发度。

两段锁协议 2PC

1633185936238

​ 在这个流程中,实际上事务B仍旧会阻塞。实际上事务 B 的 update 语句会被阻塞,直到事务 A 执行 commit 之后,事务 B 才能继续执行。 为什么?

  • 当一个事务中,具体到某个语句执行,该语句需要行锁,才会去申请

  • 当该语句执行完毕,行锁不会马上释放,而是等到整个事务结束才会释放。

  • 因此,我们在一个事务中,尽量把需要行锁的语句进行后置。

意向锁

​ 行的读写锁,功能我就不详细讲了。跟普通读写锁很类似,下面我来讲一下意向锁。为什么要有意向锁?我们可以想象一些以下这个场景:

​ 当一个事务A想对表加一个表锁。因为表锁和行锁存在一定的互斥关系,那么它就必须对全表的数据进行扫描,确定是否有行锁,如果没有行锁则进行加锁。

​ 这个效率实在太低了,难以让人接受!

解决办法

  • 如果我们在对表某个数据加行锁的时候,能不能在表上面打一个tag。
  • 当别的事务想要加表锁,看到tag就知道,目前该表中存在行锁,那么加表锁的操作就会阻塞

这种解决思路就称为意向锁。

deal lock 和 检测

解决死锁的策略

  • 阻塞超时等待,等待到一定时间放弃。
  • 死锁检测,当发生死锁后主动回滚死锁中某一个事务,让破坏死锁的条件

本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!