28.3.1.2 锁定服务 UDF 界面

本节介绍如何使用锁定服务用户定义功能(UDF)接口。要改为使用 C 语言接口,请参见第 28.3.1.1 节,“锁定服务 C 接口”。有关锁定服务接口的一般 Feature,请参见第 28.3.1 节“锁定服务”。有关用户定义函数的一般信息,请参见第 28.4.2 节“添加用户定义的功能”

28.3.1.2.1 安装或卸载 UDF 锁定界面

不需要安装第 28.3.1.1 节,“锁定服务 C 接口”中描述的锁定服务例程,因为它们已内置在服务器中。对于 Map 到对服务例程的调用的用户定义函数(UDF),情况并非如此:必须在使用前安装 UDF。本节介绍如何执行此操作。有关 UDF 安装的一般信息,请参见第 5.6.2 节“安装和卸载用户定义的函数”

锁定服务 UDF 在位于plugin_dir系统变量命名的目录中的插件库文件中实现。文件的基本名称为locking_service。文件名后缀因平台而异(例如,对于 Unix 和类 Unix 系统,为.so;对于 Windows,为.dll)。

要安装锁定服务 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;
28.3.1.2.2 使用 UDF 锁定界面

在使用锁定服务 UDF 之前,请根据第 28.3.1.2.1 节,“安装或卸载 UDF 锁定接口”提供的说明进行安装。

要获取一个或多个读锁,请调用此函数:

mysql> SELECT service_get_read_locks('mynamespace', 'rlock1', 'rlock2', 10);
+---------------------------------------------------------------+
| service_get_read_locks('mynamespace', 'rlock1', 'rlock2', 10) |
+---------------------------------------------------------------+
|                                                             1 |
+---------------------------------------------------------------+

第一个参数是锁名称空间。最后一个参数是整数超时,指示放弃之前要 await 多少秒才能获取锁。两者之间的参数是锁名称。

对于刚刚显示的示例,该函数获取具有锁标识符(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 |
+--------------------------------------+

每个锁定函数都返回非零值以 table 示成功。如果功能失败,则会发生错误。例如,发生以下错误,因为锁名不能为空:

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_lockstable,您将发现该会话拥有六个具有相同(ns, lock1)标识符的不同锁。 (有关详细信息,请参阅第 28.3.1.2.3 节“锁定服务监视”。)

因为该会话在(ns, lock1)上至少拥有一个写锁,所以其他任何会话都无法为其获取读或写锁。如果该会话仅持有该标识符的读锁,则其他会话可以为其获取读锁,但不获取写锁。

单个锁获取调用的锁是原子获取的,但是原子性在两次调用之间不成立。因此,对于如下所示的语句,其中结果集的每一行都调用一次service_get_write_locks(),则原子性适用于每个单独的调用,而不适用于整个语句:

SELECT service_get_write_locks('ns', 'lock1', 'lock2', 0) FROM t1 WHERE ... ;

Caution

由于锁定服务会针对给定锁定标识符的每个成功请求返回单独的锁定,因此单个语句就有可能获取大量锁定。例如:

INSERT INTO ... SELECT service_get_write_locks('ns', t1.col_name, 0) FROM t1;

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

28.3.1.2.3 锁定服务监控

锁定服务是使用 MySQL Server 元数据锁定框架实现的,因此您可以通过检查 Performance Schema metadata_lockstable 来监视获取或 await 的锁定服务锁定。

首先,启用元数据锁定工具:

mysql> UPDATE performance_schema.setup_instruments SET ENABLED = 'YES'
    -> WHERE NAME = 'wait/lock/metadata/sql/mdl';

然后获取一些锁并检查metadata_lockstable 的内容:

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。例如,这与使用USER LEVEL LOCKGET_LOCK()函数获取的锁不同。

锁定名称空间,名称和模式出现在OBJECT_SCHEMAOBJECT_NAMELOCK_TYPE列中。读和写锁的LOCK_TYPE值分别为SHAREDEXCLUSIVE

对于已获取的锁,LOCK_STATUS的值为GRANTED,对于正在 await 的锁,PENDING的值为。如果一个会话拥有写锁,而另一个会话正试图获取具有相同标识符的锁,您将看到PENDING

28.3.1.2.4 锁定服务 UDF 接口参考

锁定服务的 SQL 接口实现了本节中描述的用户定义功能。有关用法示例,请参见第 28.3.1.2.2 节,“使用 UDF 锁定接口”

这些功能具有以下 Feature:

  • 成功返回值非零。否则,将发生错误。

  • 命名空间和锁名称必须为非NULL,非空且最大长度为 64 个字符。

  • 超时值必须是整数,指示放弃错误之前要 await 多少秒才能获取锁。如果超时为 0,则没有 await,如果无法立即获取锁,该函数将产生错误。

这些锁定服务 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()在当前会话中获取的所有锁。

命名空间中没有锁是没有错误的。