文档首页
MySQL 9.0 参考手册
相关文档 下载此手册
PDF (US Ltr) - 40.0Mb
PDF (A4) - 40.1Mb
手册页 (TGZ) - 258.2Kb
手册页 (Zip) - 365.3Kb
信息 (Gzip) - 4.0Mb
信息 (Zip) - 4.0Mb


7.6.9.1 锁定服务

MySQL 发行版提供了一个锁定接口,它可以在两个级别访问

  • 在 SQL 级别,它是一组可加载函数,每个函数都映射到对服务例程的调用。

  • 作为 C 语言接口,可以从服务器插件或可加载函数中作为插件服务调用。

有关插件服务的常规信息,请参见 第 7.6.9 节,“MySQL 插件服务”。有关可加载函数的常规信息,请参见 添加可加载函数

锁定接口具有以下特点

  • 锁具有三个属性:锁命名空间、锁名称和锁模式

    • 锁由命名空间和锁名称的组合标识。命名空间使不同的应用程序能够使用相同的锁名称而不会发生冲突,方法是在单独的命名空间中创建锁。例如,如果应用程序 A 和 B 分别使用 ns1ns2 的命名空间,则每个应用程序都可以使用锁名称 lock1lock2 而不影响另一个应用程序。

    • 锁模式可以是读模式或写模式。读锁是共享的:如果一个会话对给定的锁标识符具有读锁,则其他会话可以对同一标识符获取读锁。写锁是排他的:如果一个会话对给定的锁标识符具有写锁,则其他会话无法对同一标识符获取读锁或写锁。

  • 命名空间和锁名称必须是非 NULL、非空且最大长度为 64 个字符。指定为 NULL、空字符串或长度超过 64 个字符的命名空间或锁名称会导致 ER_LOCKING_SERVICE_WRONG_NAME 错误。

  • 锁定接口将命名空间和锁名称视为二进制字符串,因此比较区分大小写。

  • 锁定接口提供用于获取锁和释放锁的函数。调用这些函数不需要任何特殊权限。权限检查是调用应用程序的责任。

  • 如果锁不可立即获得,则可以等待锁。锁获取调用接受一个整数超时值,该值表示在放弃之前等待获取锁的秒数。如果在超时时间内没有成功获取锁,则会发生 ER_LOCKING_SERVICE_TIMEOUT 错误。如果超时时间为 0,则不会等待,如果锁无法立即获取,则调用会产生错误。

  • 锁定接口会检测不同会话之间的锁获取调用之间的死锁。在这种情况下,锁定服务会选择一个调用者并终止其锁获取请求,并出现 ER_LOCKING_SERVICE_DEADLOCK 错误。此错误不会导致事务回滚。为了在死锁情况下选择一个会话,锁定服务优先考虑持有读锁的会话,而不是持有写锁的会话。

  • 一个会话可以使用单个锁获取调用获取多个锁。对于给定的调用,锁获取是原子的:如果所有锁都已获取,则调用成功。如果任何锁的获取失败,则调用不会获取任何锁并失败,通常会发生 ER_LOCKING_SERVICE_TIMEOUTER_LOCKING_SERVICE_DEADLOCK 错误。

  • 一个会话可以对同一个锁标识符(命名空间和锁名称组合)获取多个锁。这些锁实例可以是读锁、写锁或两者的混合。

  • 在会话中获取的锁通过调用释放锁函数显式释放,或者在会话终止时(正常终止或异常终止)隐式释放。当事务提交或回滚时,锁不会被释放。

  • 在一个会话中,当释放给定命名空间的所有锁时,它们会一起被释放。

锁定服务提供的接口不同于 GET_LOCK() 及其相关的 SQL 函数提供的接口(请参见 第 14.14 节,“锁定函数”)。例如,GET_LOCK() 没有实现命名空间,并且只提供排他锁,而不是不同的读锁和写锁。

7.6.9.1.1 锁定服务 C 接口

本节介绍如何使用锁定服务的 C 语言接口。要使用函数接口,请参见 第 7.6.9.1.2 节,“锁定服务函数接口”有关锁定服务接口的通用特性,请参见 第 7.6.9.1 节,“锁定服务”。有关插件服务的常规信息,请参见 第 7.6.9 节,“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:一个以 null 结尾的字符串,指示锁命名空间。

  • lock_names:一个以 null 结尾的字符串数组,提供要获取的锁的名称。

  • lock_numlock_names 数组中的名称数量。

  • lock_type:锁模式,可以是 LOCKING_SERVICE_READLOCKING_SERVICE_WRITE,分别用于获取读锁或写锁。

  • lock_timeout:一个整数,表示在放弃之前等待获取锁的秒数。

要释放为给定命名空间获取的锁,请调用此函数

int mysql_release_locking_service_locks(MYSQL_THD opaque_thd,
                                        const char* lock_namespace);

参数具有以下含义

  • opaque_thd:线程句柄。如果指定为 NULL,则使用当前线程的句柄。

  • lock_namespace:一个以 null 结尾的字符串,指示锁命名空间。

可以使用 Performance Schema 在 SQL 级别监视锁定服务获取或等待的锁。有关详细信息,请参见 锁定服务监控.

7.6.9.1.2 锁定服务函数接口

本节介绍如何使用其可加载函数提供的锁定服务接口。要使用 C 语言接口,请参见 第 7.6.9.1.1 节,“锁定服务 C 接口”有关锁定服务接口的通用特性,请参见 第 7.6.9.1 节,“锁定服务”。有关可加载函数的常规信息,请参见 添加可加载函数.

安装或卸载锁定服务函数接口

7.6.9.1.1 节,“锁定服务 C 接口” 中描述的锁定服务例程不需要安装,因为它们已内置到服务器中。对于映射到服务例程调用的可加载函数,情况并非如此:必须在使用之前安装这些函数。本节介绍如何执行此操作。有关可加载函数安装的常规信息,请参见 第 7.7.1 节,“安装和卸载可加载函数”.

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

要安装锁定服务函数,请使用 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';

如果在复制源服务器上使用这些函数,请在所有副本服务器上也安装它们,以避免复制问题。

安装后,函数会一直保持安装状态,直到卸载。要删除它们,请使用 DROP FUNCTION 语句

DROP FUNCTION service_get_read_locks;
DROP FUNCTION service_get_write_locks;
DROP FUNCTION service_release_locks;
使用锁定服务函数接口

在使用锁定服务函数之前,请根据 安装或卸载锁定服务函数接口 中提供的说明安装它们。

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

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_SCHEMAOBJECT_NAMELOCK_TYPE 列中。读锁和写锁的 LOCK_TYPE 值分别为 SHAREDEXCLUSIVE

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

锁定服务接口函数参考

锁定服务的 SQL 接口实现了本节中描述的可加载函数。有关使用示例,请参见 使用锁定服务函数接口.

这些函数共享以下特征

  • 返回值在成功时为非零值。否则,会发生错误。

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

  • 超时值必须是整数,表示在放弃并发生错误之前等待获取锁的秒数。如果超时为 0,则不会等待,并且如果无法立即获取锁,则函数会产生错误。

这些锁定服务函数可用