Documentation Home
MySQL 8.4 Reference Manual
Related Documentation Download this Manual
PDF (US Ltr) - 39.8Mb
PDF (A4) - 39.9Mb
Man Pages (TGZ) - 257.9Kb
Man Pages (Zip) - 364.9Kb
Info (Gzip) - 4.0Mb
Info (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_num: lock_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终止字符串,表示锁定命名空间。

锁定服务获取或等待的锁定可以在 SQL 层面使用 Performance Schema 监控。详见Locking Service Monitoring

7.6.9.1.2 锁定服务 Function Interface

本节描述了如何使用锁定服务接口提供的可加载函数。要使用C语言接口,请见第7.6.9.1.1节,“The Locking Service C Interface”。关于锁定服务接口的总体特征,请见第7.6.9.1节,“The Locking Service”。关于可加载函数的总体信息,请见添加可加载函数

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

描述在第7.6.9.1.1节,“锁定服务C接口”中提到的锁定服务例程不需要安装,因为它们已经内置在服务器中。同样,不是所有映射到服务例程调用的可加载函数都可以不安装:这些函数必须在使用前安装。这一节描述了如何进行安装。关于可加载函数安装的总体信息,请见第7.7.1节,“安装和卸载可加载函数”

锁定服务例程实现在名为plugin_ dir系统变量指定的插件库文件中。文件基础名称是locking_service。文件名后缀因平台而异(例如,.so用于Unix和Unix-like系统,.dll用于Windows)。

要安装锁定服务例程,请使用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)标识符。 (详见Locking Service Monitoring。)

由于该会话至少持有一次写锁定(ns, lock1),因此其他会话不能获取对其的读或写锁定。如果该会话仅持有该标识符的读锁定,其他会话可以获取对其的读锁定,但不能获取写锁定。

对于单个锁定获取调用,锁定是原子地获取的,但是原子性不适用于多个调用。因此,对于以下语句,whereservice_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;

这些类型的语句可能具有某些不良影响。例如,如果语句在执行过程中失败并回滚,到达失败点之前获取的锁定仍然存在。如果意图是将插入的行与获取的锁定对应,那么这个意图不能满足。此外,如果需要确保锁定按特定的顺序授予,请注意结果集顺序可能因优化器选择的执行计划而不同。因此,对于这些原因,可能最好限制应用程序到单个锁定获取调用语句。

锁定服务监控

锁定服务是使用 MySQL 服务器元数据锁定框架实现的,因此可以通过 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 接口实现了本节中描述的可加载函数。使用示例,请参见Locking Service Function Interface 使用

这些函数共享以下特征:

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

  • 命名空间和锁名称必须是非NULL、非空且长度不超过 64 个字符。

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

这些锁定服务函数可用: