12.14 锁定功能

本节介绍用于操作用户级锁的功能。

table12.18 锁定功能

NameDescription
GET_LOCK()获取命名锁
IS_FREE_LOCK()命名锁是否免费
IS_USED_LOCK()是否使用了命名锁;如果为 true,则返回连接标识符
RELEASE_ALL_LOCKS()释放所有当前命名的锁
RELEASE_LOCK()释放命名的锁

GET_LOCK(str,timeout)

尝试使用* timeout 秒的超时时间来获得具有由字符串 str *给出的名称的锁。 * timeout *为负值 table 示无限超时。锁是排他的。当一个会话举行时,其他会话无法获得相同名称的锁。

如果成功获得了锁定,则返回1;如果尝试超时(例如,因为另一个 Client 端先前已锁定该名称),则返回0;如果发生错误(例如内存不足或线程被mysqladmin kill杀死),则返回NULL。 。

通过GET_LOCK()显式地释放通过GET_LOCK()获得的锁,或者在会话终止(正常或异常)时隐式释放。提交或回滚事务时,不会释放通过GET_LOCK()获得的锁。

在 MySQL 5.7 中,使用元数据锁定(MDL)子系统重新实现了GET_LOCK(),并扩展了其功能。可以同时获取多个锁,并且GET_LOCK()不会释放任何现有锁。

给定的会话甚至有可能获得相同名称的多个锁。其他会话无法获取具有该名称的锁,直到获取会话释放该名称的所有锁。

重新实现 MDL 的结果是,使用GET_LOCK()获得的唯一命名的锁出现在 Performance Schema metadata_lockstable 中。 OBJECT_TYPE列 table 示USER LEVEL LOCK,而OBJECT_NAME列 table 示锁名称。在为相同名称获取多个锁的情况下,仅该名称的第一个锁在metadata_lockstable 中注册一行。该名称的后续锁会在该锁中增加一个计数器,但不会获取其他元数据锁。释放名称上的最后一个锁实例后,将删除该锁的metadata_locks行。

获取多个锁的能力意味着 Client 端之间可能会出现死锁。发生这种情况时,服务器选择一个呼叫者,并以ER_USER_LOCK_DEADLOCK错误终止其锁定获取请求。此错误不会导致事务回滚。

在 MySQL 5.7 之前,只能获取一个同时锁定,并且GET_LOCK()释放任何现有的锁定。通过以下示例可以看出,自 MySQL 5.7 起,锁获取行为有所不同。假设您执行以下语句:

SELECT GET_LOCK('lock1',10);
SELECT GET_LOCK('lock2',10);
SELECT RELEASE_LOCK('lock2');
SELECT RELEASE_LOCK('lock1');

在 MySQL 5.7 或更高版本中,第二个GET_LOCK()获得第二个锁,并且两个RELEASE_LOCK()调用均返回 1(成功)。在 MySQL 5.7 之前,第二个GET_LOCK()释放第一个锁('lock1'),第二个RELEASE_LOCK()返回NULL(失败),因为没有'lock1'要释放。

MySQL 5.7 和更高版本对 64 个字符的锁名强制使用最大长度。以前,没有强制执行任何限制。

GET_LOCK()可用于实现应用程序锁定或模拟记录锁定。名称在服务器范围内被锁定。如果一个会话中已锁定名称,则GET_LOCK()阻止另一会话对具有相同名称的锁的任何请求。这使同意给定锁名称的 Client 端可以使用该名称执行协作建议锁定。但是请注意,它还使不在协作 Client 端集合中的 Client 端意外或故意锁定名称,从而防止任何协作 Client 端锁定该名称。减少这种可能性的一种方法是使用特定于数据库或特定于应用程序的锁名。例如,使用格式为* db_name.str app_name.str *的锁名。

如果多个 Client 端正在 await 锁,则获取它们的 Sequences 是不确定的。应用程序不应假定 Client 端将按照发出锁定请求的 Sequences 来获得锁定。

GET_LOCK()对于基于语句的复制不安全。如果在binlog_format设置为STATEMENT时使用此功能,则会记录一条警告。

Caution

具有获取多个命名锁的能力,单个语句就有可能获取大量锁。例如:

INSERT INTO ... SELECT GET_LOCK(t1.col_name) FROM t1;

这些类型的声明可能会产生某些不利影响。例如,如果该语句在执行过程中部分失败并回滚,则直到失败点为止获取的锁仍将存在。如果意图是在插入的行和获取的锁之间存在对应关系,则将无法满足该意图。同样,如果以一定 Sequences 授予锁很重要,请注意结果集 Sequences 可能会有所不同,具体取决于优化器选择的执行计划。由于这些原因,最好将应用程序限制为每个语句只能进行一次锁定获取调用。

可以使用其他锁定接口作为插件服务或一组用户定义的函数。与GET_LOCK()和相关函数提供的接口不同,此接口提供了锁命名空间和不同的读写锁。有关详细信息,请参见第 28.3.1 节“锁定服务”

IS_FREE_LOCK(str)

检查名为* str *的锁是否可以免费使用(即未锁定)。如果锁是空闲的(没有人正在使用锁),则返回1;如果正在使用锁,则返回0;如果发生错误(例如不正确的参数),则返回NULL

此功能对于基于语句的复制不安全。如果在binlog_format设置为STATEMENT时使用此功能,则会记录一条警告。

IS_USED_LOCK(str)

检查名为* str *的锁是否正在使用(即已锁定)。如果是这样,它将返回持有该锁的 Client 端会话的连接标识符。否则,它返回NULL

此功能对于基于语句的复制不安全。如果在binlog_format设置为STATEMENT时使用此功能,则会记录一条警告。

RELEASE_ALL_LOCKS()

释放当前会话持有的所有命名锁,并返回释放的锁数(如果没有锁,则返回 0)

此功能对于基于语句的复制不安全。如果在binlog_format设置为STATEMENT时使用此功能,则会记录一条警告。

RELEASE_LOCK(str)

释放由GET_LOCK()获得的字符串* str *命名的锁。如果释放了锁,则返回1;如果不是由该线程构建的锁(在这种情况下不释放锁),则返回0;如果命名的锁不存在,则返回NULL。如果从未通过调用GET_LOCK()获得该锁,或者该锁先前已被释放,则该锁不存在。

DO语句与RELEASE_LOCK()结合使用非常方便。参见第 13.2.3 节“ DO 语句”

此功能对于基于语句的复制不安全。如果在binlog_format设置为STATEMENT时使用此功能,则会记录一条警告。