本文地址:https://www.askmac.cn/archives/mysql-lock.html
第7章 锁
章节概述
本章介绍如何MySQL的锁(Lock)机制。你会了解:
- 锁的概念
- 如何使用显式表锁
- 如何使用协同(advisory)锁
7.1 锁的概念
MySQL服务端使用多线程架构,这样使其能够并行地位多个客户端进行服务。对连接的每个客户端来说,服务端分配了一个线程作为其连接处理。如果每个客户端访问不同的表,它们并不会互相干扰对方。然而,当多个客户端尝试在同一时间访问同一张表,会产生争用并需要客户端间协调。如,一个客户端正在修改行而另一个客户端正在读取它们,或两个客户端正在同时修改同一行,这样都会产生问题。为了避免这些问题引发的数据讹误,MySQL使用锁来进行解决。
锁是一种避免由于多个客户端同时进行数据访问而引发问题的机制。锁由服务端管理:它会为某个客户端对数据上锁以限制其它客户端对此数据的访问,直到锁被释放。锁仅允许持有它的客户端对被锁的数据进行访问,而对其它对此数据进行争用的客户端限制访问的操作行为。锁机制所起到的效果是为了在多个客户端进行有冲突的操作时,进行等待,以达到顺序化的数据访问。
不是所有并行访问都会产生冲突,因此使用什么类型的锁以允许客户端访问数据则取决于客户端到底是希望进行读还是写:
- 如果一个客户端希望读数据,其它客户端也希望读这同样的数据,这并不会产生冲突,它们可以在同时进行读取。然而,如果其它客户端如果要进行写(修改)数据的话,就需要等到读取完成才能进行。
- 如果一个客户端希望写数据,所有其它客户端必须等它写完,不管它们要做的操作是写操作还是读操作。
换句话说,一个读取器一定会阻碍写,但是不会阻止其它的读取操作。而写则会对其它读和写都进行阻止。读锁和写锁的作用就是允许这些限制被实现。锁使得客户保持等待直到可以安全进行。在这种方式下,锁通过不允许并行冲突来保证避免数据讹误,以有序的方式读取被改变的数据。
7.1.1 隐式锁和显式锁
锁在数据上可以隐式获取或显式获取:
- 对于一个进行并不进行操作刻意去获取锁的客户端,MySQL服务端为了安全地处理客户端语句,会去隐式地获取锁。例如,当客户端对一张表进行读或写时,存储引擎会隐式地使用锁来避免冲突。
- 在某些情况下,如果隐式锁不足以达到客户端的目的,则可以使用lock tables命令和unlock tables来显式获取锁和释放锁。当客户端需要一次性执行多行语句操作且必须不被其它客户端打断时,这种显式锁是必要的。例如,应用从一张表查某个值并使用它来判断在其它表中哪些值需要进行更新。使用隐式锁的话,其它客户端执行的操作可能会在最先的客户端进行操作的语句之间的间隙对数据进行访问,进而产生冲突。为了此类问题,就需要使用显式地在表上加锁。
注意:lock tables命令对InnoDB或Falcon引擎没有使用意义。
另一种锁类型为协同锁(advisory / cooperative lock)锁。协同锁并不会锁数据,除非客户端之间协同达到一定程度,才会出手避免其它客户端对数据进行访问。不像隐式锁和显式锁,协同锁并不由服务端管理。客户端通过使用一系列功能调用来管理协同锁从而使得互相之间进行协同。
MySQL上的数据锁可以在不同级别产生。显式锁要求使用lock tables命令来进行表级锁。而隐式锁,其锁的级别取决于所使用的存储引擎:
- MyISAM和MEMORY表的锁是表级别锁。
- InnoDB表的锁则是行级别锁。
7.1.2 锁的粒度
不同级别锁的“粒度”拥有不同的并行特性:
- 表级锁并非可以像页锁定或行锁定一样在混合读/写环境下进行并行操作。表锁会阻止其它客户端对表进行数据修改,即便其它客户端所需修改的表数据部分和持有锁的客户端所要修改的表数据不重合也不行。对于页锁和行锁,其持有锁的客户端并不会阻止其它客户端修改表中的其它页和行。
- 死锁这类情况不会发生在非事务引擎(使用表级锁)上。如,使用行级锁,两个客户端各在不同行上的获取了锁,如果它们又尝试修改对方持有锁的行,那么它们两个都无法继续进行操作,这就是“死锁”。使用表级锁,服务端会判断什么锁是所需的并在执行语句前获取它们,所以死锁永远不会发生。(当然也会有例外,当应用使用游标时,因为服务端必须对执行多行语句的客户端所打开的游标持有锁。假设客户端1打开了一个游标并读取表1,而客户端2打开了一个游标读取表2,当两个游标都打开时,如果每个客户端都尝试更新对方锁住的表,则会发生死锁)。
Comment