MySQL发行版提供了一个可在两个级别访问的锁定接口:
- 在SQL级别上,作为一组映射到对服务例程的调用的用户定义函数(UDF)。
- 作为C语言接口,可以从服务器插件或用户定义的函数作为插件服务调用。
有关插件服务的一般信息,请参见 第5.6.8节“ MySQL插件服务”。有关用户定义函数的一般信息,请参见添加用户定义函数。
锁定接口具有以下特征:
- 锁具有三个属性:锁命名空间,锁名称和锁模式:
- 锁由名称空间和锁名称的组合来标识。命名空间使不同的应用程序可以使用相同的锁名,而不会在单独的命名空间中创建锁而导致冲突。例如,如果应用程序A和B使用的名称空间
ns1和ns2,分别,每个应用可以使用锁的名称lock1和lock2不与其它应用干扰。 - 锁定模式为读或写。共享读取锁:如果某个会话在给定的锁标识符上具有读取锁,则其他会话可以在同一标识符上获取读锁。写锁是互斥的:如果一个会话在给定的锁标识符上具有写锁,则其他会话无法在同一标识符上获取读或写锁。
- 锁由名称空间和锁名称的组合来标识。命名空间使不同的应用程序可以使用相同的锁名,而不会在单独的命名空间中创建锁而导致冲突。例如,如果应用程序A和B使用的名称空间
- 命名空间和锁名称必须为非
NULL,非空,并且最大长度为64个字符。指定为的名称空间或锁名称NULL,空字符串或长度超过64个字符的字符串会导致ER_LOCKING_SERVICE_WRONG_NAME错误。 - 锁定接口将名称空间和锁定名称视为二进制字符串,因此比较区分大小写。
- 锁定接口提供获取锁定和释放锁定的功能。调用这些功能不需要特殊特权。特权检查是调用应用程序的责任。
- 如果没有立即可用,可以等待锁。锁获取调用采用整数超时值,该值指示放弃之前要等待多少秒才能获取锁。如果没有成功获取锁而达到超时,
ER_LOCKING_SERVICE_TIMEOUT则会发生错误。如果超时为0,则没有等待,如果无法立即获取锁,则调用将产生错误。 - 锁定接口检测不同会话中的锁定获取调用之间的死锁。在这种情况下,锁定服务会选择一个调用方并以
ER_LOCKING_SERVICE_DEADLOCK错误终止其锁定获取请求 。此错误不会导致事务回滚。要选择发生死锁的会话,锁定服务将优先选择持有读锁定的会话而不是持有写锁定的会话。 - 一个会话可以通过单个锁获取调用获取多个锁。对于给定的呼叫,锁获取是原子的:如果获取了所有锁,则该呼叫成功。如果获取任何锁均失败,则该调用将不获取锁并失败,通常会带有
ER_LOCKING_SERVICE_TIMEOUT或ER_LOCKING_SERVICE_DEADLOCK错误。 - 会话可以为同一锁标识符(名称空间和锁名称组合)获取多个锁。这些锁实例可以是读锁,写锁或两者的混合。
- 会话中获取的锁通过调用释放锁函数显式释放,或者在会话终止时(正常或异常)隐式释放。事务提交或回滚时不会释放锁。
- 在会话中,给定名称空间的所有锁在释放时都会一起释放。
锁定服务提供的接口不同于GET_LOCK()SQL函数和相关的SQL函数提供的接口(请参见第12.15节“锁定函数”)。例如,GET_LOCK()不实现名称空间,仅提供排他锁,不提供不同的读写锁。
5.6.8.1.1锁定服务C接口
本节介绍如何使用锁定服务C语言接口。要改用UDF接口,请参见 第5.6.8.1.2节“锁定服务UDF接口”。有关锁定服务接口的一般特性,请参见 第5.6.8.1节“锁定服务”。有关插件服务的一般信息,请参见第5.6.8节“ MySQL插件服务”。
使用锁定服务的源文件应包括以下头文件:
#include <mysql/service_locking.h>
要获取一个或多个锁,请调用此函数:
int mysql_acquire_locking_service_locks(MYSQL_THD opaque_thd,
const char* lock_namespace,
const char**lock_names,
size_t lock_num,
enum enum_locking_service_lock_type lock_type,
unsigned long lock_timeout);
自变量具有以下含义:
opaque_thd:线程句柄。如果指定为NULL,则使用当前线程的句柄。lock_namespace:以空值结尾的字符串,指示锁定名称空间。lock_names:以空值结尾的字符串数组,提供要获取的锁的名称。lock_num:lock_names数组中名称的数量 。lock_type:锁定模式,分别为LOCKING_SERVICE_READ或LOCKING_SERVICE_WRITE获取读锁定或写锁定。lock_timeout:放弃前等待获取锁的整数秒数。
要释放为给定名称空间获取的锁,请调用此函数:
int mysql_release_locking_service_locks(MYSQL_THD opaque_thd,
const char* lock_namespace);
自变量具有以下含义:
opaque_thd:线程句柄。如果指定为NULL,则使用当前线程的句柄。lock_namespace:以空值结尾的字符串,指示锁定名称空间。
可以使用性能模式在SQL级别上监视锁定服务获取或等待的锁定。有关详细信息,请参阅锁定服务监视。
5.6.8.1.2锁定服务UDF接口
本节介绍如何使用锁定服务用户定义功能(UDF)接口。要改为使用C语言接口,请参见 第5.6.8.1.1节“锁定服务C接口”。有关锁定服务接口的一般特征,请参见 第5.6.8.1节“锁定服务”。有关用户定义函数的一般信息,请参见 添加用户定义函数。
安装或卸载UDF锁定接口
不需要安装第5.6.8.1.1节“ Locking Service C接口”中 描述的锁定服务例程 ,因为它们已内置在服务器中。对于映射到对服务例程的调用的用户定义函数(UDF),情况并非如此:必须在使用前安装UDF。本节介绍如何执行此操作。有关UDF安装的一般信息,请参见 第5.7.1节“安装和卸载用户定义的功能”。
锁定服务UDF在plugin_dir系统变量命名的目录中的插件库文件中实现 。文件的基本名称为locking_service。每个平台的文件名后缀都不同(例如, .so对于Unix和类似Unix的系统, .dll对于Windows)。
要安装锁定服务UDF,请使用以下 CREATE FUNCTION语句,并.so根据需要调整平台的后缀:
CREATE FUNCTION service_get_read_locks RETURNS INT
SONAME 'locking_service.so';
CREATE FUNCTION service_get_write_locks RETURNS INT
SONAME 'locking_service.so';
CREATE FUNCTION service_release_locks RETURNS INT
SONAME 'locking_service.so';
如果在源复制服务器上使用了UDF,请在所有副本服务器上也安装它们,以避免复制问题。
一旦安装,UDF将保持安装状态直到被卸载。要删除它们,请使用以下DROP FUNCTION语句:
DROP FUNCTION service_get_read_locks;
DROP FUNCTION service_get_write_locks;
DROP FUNCTION service_release_locks;
使用UDF锁定界面
在使用锁定服务UDF之前,请按照“ 安装或卸载UDF锁定接口”中提供的说明进行 安装。
要获取一个或多个读锁,请调用此函数:
mysql> SELECT service_get_read_locks('mynamespace', 'rlock1', 'rlock2', 10);
+---------------------------------------------------------------+
| service_get_read_locks('mynamespace', 'rlock1', 'rlock2', 10) |
+---------------------------------------------------------------+
| 1 |
+---------------------------------------------------------------+
第一个参数是锁名称空间。最后一个参数是整数超时,指示放弃之前要等待多少秒才能获取锁。两者之间的参数是锁名称。
对于刚刚显示的示例,该函数获取具有锁标识符(mynamespace, rlock1) 和的锁(mynamespace, rlock2)。
要获取写锁而不是读锁,请调用此函数:
mysql> SELECT service_get_write_locks('mynamespace', 'wlock1', 'wlock2', 10);
+----------------------------------------------------------------+
| service_get_write_locks('mynamespace', 'wlock1', 'wlock2', 10) |
+----------------------------------------------------------------+
| 1 |
+----------------------------------------------------------------+
在这种情况下,锁定标识符为 (mynamespace, wlock1)和 (mynamespace, wlock2)。
要释放名称空间的所有锁,请使用以下函数:
mysql> SELECT service_release_locks('mynamespace');
+--------------------------------------+
| service_release_locks('mynamespace') |
+--------------------------------------+
| 1 |
+--------------------------------------+
每个锁定函数都返回非零值以表示成功。如果功能失败,则会发生错误。例如,发生以下错误,因为锁名不能为空:
mysql> SELECT service_get_read_locks('mynamespace', '', 10);
ERROR 3131 (42000): Incorrect locking service lock name ''.
会话可以为同一锁标识符获取多个锁。只要不同的会话没有标识符的写锁,该会话就可以获取任意数量的读或写锁。对标识符的每个锁定请求都获得一个新的锁定。以下语句获取具有相同标识符的三个写锁,然后为相同标识符获取三个读锁:
SELECT service_get_write_locks('ns', 'lock1', 'lock1', 'lock1', 0);
SELECT service_get_read_locks('ns', 'lock1', 'lock1', 'lock1', 0);
如果此时检查Performance Schema metadata_locks表,您将发现该会话拥有六个具有相同(ns, lock1)标识符的不同锁。(有关详细信息,请参阅锁定服务监视。)
因为该会话至少持有一个写锁 (ns, lock1),所以其他任何会话都无法为其获取锁,无论是读还是写。如果该会话仅持有该标识符的读锁,则其他会话可以为其获取读锁,但不获取写锁。
单个锁获取调用的锁是原子获取的,但是原子性在两次调用之间不成立。因此,对于以下语句 service_get_write_locks()(在结果集的每一行中调用一次)而言,原子性对于每个单独的调用均有效,但对于整个语句而言不成立:
SELECT service_get_write_locks('ns', 'lock1', 'lock2', 0) FROM t1 WHERE ... ;
警告
由于锁定服务会针对给定锁定标识符的每个成功请求返回单独的锁定,因此单个语句就有可能获取大量锁定。例如:
INSERT INTO ... SELECT service_get_write_locks('ns', t1.col_name, 0) FROM t1;
这些类型的声明可能会产生某些不利影响。例如,如果该语句在执行过程中部分失败并回滚,则直到失败点为止获取的锁仍将存在。如果意图是在插入的行和获取的锁之间存在对应关系,则将无法满足该意图。同样,如果以一定顺序授予锁很重要,请注意结果集顺序可能会有所不同,具体取决于优化器选择的执行计划。由于这些原因,最好将应用程序限制为每个语句只能进行一次锁定获取调用。
锁定服务监控
锁定服务是使用MySQL Server元数据锁定框架实现的,因此您可以通过检查Performance Schema metadata_locks表来监视获取或等待的锁定服务锁定。
首先,启用元数据锁定工具:
mysql> UPDATE performance_schema.setup_instruments SET ENABLED = 'YES'
-> WHERE NAME = 'wait/lock/metadata/sql/mdl';
然后获取一些锁并检查metadata_locks表的内容 :
mysql> SELECT service_get_write_locks('mynamespace', 'lock1', 0);
+----------------------------------------------------+
| service_get_write_locks('mynamespace', 'lock1', 0) |
+----------------------------------------------------+
| 1 |
+----------------------------------------------------+
mysql> SELECT service_get_read_locks('mynamespace', 'lock2', 0);
+---------------------------------------------------+
| service_get_read_locks('mynamespace', 'lock2', 0) |
+---------------------------------------------------+
| 1 |
+---------------------------------------------------+
mysql> SELECT OBJECT_TYPE, OBJECT_SCHEMA, OBJECT_NAME, LOCK_TYPE, LOCK_STATUS
-> FROM performance_schema.metadata_locks
-> WHERE OBJECT_TYPE = 'LOCKING SERVICE'\G
*************************** 1. row ***************************
OBJECT_TYPE: LOCKING SERVICE
OBJECT_SCHEMA: mynamespace
OBJECT_NAME: lock1
LOCK_TYPE: EXCLUSIVE
LOCK_STATUS: GRANTED
*************************** 2. row ***************************
OBJECT_TYPE: LOCKING SERVICE
OBJECT_SCHEMA: mynamespace
OBJECT_NAME: lock2
LOCK_TYPE: SHARED
LOCK_STATUS: GRANTED
锁定服务锁的OBJECT_TYPE 值为LOCKING SERVICE。例如GET_LOCK(),这与通过函数获取的具有的锁 OBJECT_TYPE不同USER LEVEL LOCK。
锁命名空间,名称和模式出现在 OBJECT_SCHEMA, OBJECT_NAME和 LOCK_TYPE列。读和写锁的LOCK_TYPE值分别为 SHARED和EXCLUSIVE。
该LOCK_STATUS值 GRANTED用于获取的锁, PENDING对于正在等待的锁。您将看到PENDING一个会话是否持有写锁,而另一会话是否正在尝试获取具有相同标识符的锁。
锁定服务UDF接口参考
锁定服务的SQL接口实现了本节中描述的用户定义功能。有关用法示例,请参阅使用UDF锁定接口。
这些功能具有以下特征:
- 成功返回值非零。否则,将发生错误。
- 命名空间和锁名称必须为非
NULL,非空,并且最大长度为64个字符。 - 超时值必须是整数,指示放弃错误之前要等待多少秒才能获取锁。如果超时为0,则没有等待,如果无法立即获取锁,该函数将产生错误。
这些锁定服务UDF可用:
-
service_get_read_locks(*namespace*, *lock_name*[, *lock_name*\] ..., *timeout*)使用给定的锁名获取给定名称空间中的一个或多个读取(共享)锁,如果未在给定的超时值内获取锁,则超时并出错。
-
service_get_write_locks(*namespace*, *lock_name*[, *lock_name*\] ..., *timeout*)使用给定的锁名获取给定名称空间中的一个或多个写(排他)锁,如果未在给定的超时值内获取锁,则超时并出错。
-
service_release_locks(*namespace*)对于给定的名称空间,使用
service_get_read_locks()和 释放在当前会话中获取的所有锁service_get_write_locks()。命名空间中没有锁是没有错误的。




