7.2 显式(Explicit)表锁
客户端可以使用两个语句来管理显式表锁。LOCK TABLES命令会获取表锁,而UNLOCK TABLES命令则会释放锁。对于显式锁的获取,我们可以具体在某些场景中使用:
一个隐式锁仅在对于(非事务引擎下的)单个查询执行时间内起到作用。但是对于希望运行多语句跟新且同时不被其它客户端干扰的情况,则不适用。而在执行由事务引擎支持的事务时,锁可以在整个事务时间范围起作用,为了达到这样的目的,你可以获取一个显式锁来维持其效果直到你释放它,这样其它客户端就不能修改你锁住的那些表了。
使用显式锁,可以在将多个语句作为一组进行执行时发挥作用,从而提高效率。首先,服务端减少了获取和释放锁的工作,因为其不必未每个语句都进行其锁的操作。它仅是在操作的最开始时候获取所有所需的锁,并在事务最后释放它们即可。其二,对于那些修改数据的语句,索引刷新操作次数降低了。例如,如果你执行多个INSERT语句,且都使用隐式锁,那么索引的刷新操作将在每个语句之后进行。如果你对表使用了显式锁,那么索引的刷新仅会在所有插入操作完成并释放锁后进行,这样所产生的磁盘活动就更少了。
LOCK TABLES语句可以对指定的表进行锁定,并制定索要获取的锁的类型。如,以下语句会在Country表上要求获取读锁,并对City表要求获取写锁:
mysql> LOCK TABLES Country READ, City WRITE;
为了能成功执行lock tables命令,你必须要有lock tables权限,且需要每个被锁表的select权限。
如果所要锁定的表正在被使用,那么执行lock tables会陷入等待,在获取到所需的锁之前它都不会返回。
如果你需要使用多表的同时持有显式锁,你就必须在同时锁定它们,因为你不能在持有显式锁是使用任意未被锁定的表。同样,你需要使用单个lock tables命令来锁定这些表。注意:lock tables会释放你之前已经持有的锁,因此你不应该为了获取多个表锁而去多次执行此命令。
以下列出了可用的锁类型和它的影响:
READ
读锁,为了读取表而进行的锁定。READ锁会对读取查询如select语句查询的表进行加锁。这种锁不允许如insert, delete,或update等表的写操作,这甚至对持有此锁的客户端也是如此。当表被READ锁锁住,其它客户端则只能在同时进行对此表的读取操作,写是不行的。如果有客户端希望对这张表进行写操作,就必须等此被读锁住的表上所有正在读取此表的操作结束并释放了锁后,才能进行。READ被认为是一个共享锁。
WRITE
写锁。WRITE锁是一个排它锁。仅当对应的表不正被使用时,才能获取此锁。一旦获取成功,仅持有此锁的客户端才能对此表进行读和写操作。其它客户端则对此表既不能读也不能写,当然更不能为了读写而进行加锁操作了。
READ LOCAL
为了读取数据而锁表,不过允许表的并行插入。对于【读阻塞写】这一原则来说,并行插入是一个例外。如果一张MyISAM表中数据中没有由于大量删除或更新导致空洞存在的话,那么insert操作的数据会添加在表的末尾。在这样的情况下,正在读取此表的客户端可以使用READ LOCAL锁来允许其它客户端对此表进行插入操作。如果,MyISAM表中有空洞,你需要先使用optimize table命令来去除表中的碎片。READ LOCAL被做为共享锁来使用。
为了释放显式锁,可执行unlock tables语句。因为它会释放所有客户端所发起的显式锁,因此这个语句不需要指定表名。
由一个客户端所持有的显式锁也可以通过此客户端执行另一个lock tables语句来进行释放。锁不能跨连接维护;如果一个客户端在其中止时有任何未被释放的锁,服务端会隐式地为其释放此锁。当然,拥有SUPER权限的管理员也可以使用KILL语句来终止此客户端连接,这会使得由客户端持有的锁被释放。
在非以上情况下,仅那个使用lock tables语句来获取到锁的客户端才能释放锁。其它客户端不能释放它们。换句话说,如果你获取了锁,除非你放弃掉它,否则它将一直是你的。其它客户端不能强制你释放它。
表锁可以被事务所影响。使用start transaction语句启动一个事务时会隐式地进行unlock tables。而在执行lock tables语句会隐式地提交所有在等待的(pending)事务。如果你已经锁定了某些表,执行unlock tables语句也会隐式提交所有在等待的(pending)事务。
注意:另外lock tables, select … in share lock mode和select … for update也会进行显式地表/行锁定。