Mysql 百问系列:认识锁

950 阅读4分钟

由于锁的内容涉及比较多,会分多篇文章讲解。本文主要从锁的种类讲解下,先搞清楚到底有哪些锁然后我们后面再根据不同锁不同问题来详细说明。所以现在先把精力放在搞清楚到底有哪些锁

问题:

  1. 锁分哪几种?
  2. 不同锁各自用处?

根据加锁的范围可以分为:

  • 全局锁
  • 表级锁
  • 行级锁
针对这个三类锁,需要明白一下几个问题:
  1. 全局锁怎么加? 什么时候加全局锁?
Flush tables with read lock;

加上全局所后,整个数据库就是只读的了。 其他客户端连接后无法修改数据。需要注意的是,如果加锁的客户端断开连接,那么锁就会解除。
如果需要备份整个数据库,那么可以使用全局锁,使在备份过程中只允许读,保障备份数据的一致性。
虽然全局锁可以保障备份数据时候的一致性,但是由于变成只读,那么可定会影响业务的进行。我们也没见过因为备份数据库导致线上服务无法正常工作啊。所以一般情况下,生成中的确很少用到这个功能,取而代之的是mysqldump 工具,使用–single-transaction 参数,由于Innodb 的MVCC能力,所以可以保障备份事数据一致性的同时又不上锁。(这也是InnoDb 基本取代MyISAM 引擎的原因之一)

  1. 表级锁什么时候加? 什么场景需要用到表级别锁?

我们都知道MyISAM 与 InnoDB 的一个区别是,MyISAM支持的是表级锁,而InnoDB可以支持行级锁,所以InnoDB 的并发性更好一点。 但是这不意味着这InnoDB中没有表级锁。InnoDB 同样存在表级别锁

先来看这样一个场景,我有一张student 表,其中字段有 id,name ,age 三个字段。表中有100万条数据。假设现在有个长事务正在进行,正在给student表其中5万人审核年龄并修改成正确年龄。如果这个事务要持续1小时。期间,我们同时需要更新表结构,给student表添加一个address字段。
我们会发现表结构更新语句会被阻塞,需要等到长事务结束才更新表结构。
那么更新表结构是被什么阻塞呢? 显而易见的时事务更新对应数据的年龄,那么其中有5万条数据加上了行级锁。是被这个行级锁阻塞的的吗?其实不是。 这里Mysql 引入了MDL(metadata lock) 元数据锁。简单来说他是用来锁表的,是个表级别锁。MDL也分为读锁和写锁。
例子中修改数据时,开始前会先给表student 加 MDL 的读锁来锁住整个表,防止表中数据在查询,修改的时候表的结构被改变。由于我们需要给student表添加字段的时候会给表添加MDL的写锁。这是MDL写锁只能等待MDL读锁被释放才能加上去而被阻塞。
概况下MDL 的加锁规律就是: 表做查询,修改,新增,删除时会对表先加MDL的读锁,然后再加对应的行锁。 而对表结构做修改时,会对表加MDL的写锁

  1. 行级锁有哪些?
  • Record Locks (记录锁) 可以分为读(S)记录锁,和写(X)记录锁。用来锁住记录
  • Gap Locks (间隙锁),用来防止幻影
  • Next-Key Locks (Next-Key 锁),记录锁和间隙锁的合体
  • Insert Intention Locks (插入意向锁)

要讲解行数的加锁机制比较麻烦,后续另一篇文章详细聊。这里我们只要知道InnoDB支持行锁,而MyISAM 是不支持的。

根据锁的兼容性来分

  • 共享锁 Shared Locks(S 锁)
  • 独占锁 Exclusive Locks(X 锁)

共享锁可以同时获取,而独占锁至于在对象上没有存在任何锁的情况下可以获取
事务A, 查询 表student 中id 为1,2,3的数据,那么事务A就分别给数据1,2,3添加了共享行锁。
事务B,  查询 表student 中id 为1,3,5的数据,那么事务B就分别给数据1,3,5添加了共享行锁。
如果事务A,B都未提交,这时候数据1,3分别挂了2把共享锁。
事务C,  想要修改表student id 为 1 的数据,那么他需要给数据1挂上独占锁,由于1已经有共享锁存在,他被挂起等待。
由于共享锁和独占锁的特性,就会产生一个死锁问题,这个问题我也会另开一篇详细说明。

总结:

根据锁的加锁范围我们可以有 全局锁,表锁(MDL为表锁的一种),行锁。而行锁又细分成了几种不同的锁。 同时不管是表锁,还是行锁可能根据兼容性来分,又会有共享表锁,独占表锁,共享行锁,独占行锁等