首页 慕课教程 MySQL 进阶教程 MySQL 进阶教程 MySQL 服务器级别的锁等待

MySQL 服务器级别的锁等待

使用锁来控制资源共享的应用系统,如何处理锁的竞争问题是个头疼事。MySQL 有两个级别的锁等待,服务器级别和存储引擎级别,本节重点介绍服务器级别的锁等待。

1. 表锁

表锁可以是显式的,也可以是隐式的。

1.1 显式锁

通过 lock tables和unlock tables 可以控制显式锁。在 MySQL 会话中执行 lock tables 命令,在表customer上会获得一个显式锁。

mysql> lock tables customer read;
Query OK, 0 rows affected (0.00 sec)

在 MySQL 另一个会话中,对表 customer 执行 lock tables 命令,查询会挂起。

mysql> lock tables customer write;

在第一个会话中执行 show processlist 查看线程状态,可以看到线程 13239868 的状态为 Waiting for table metadata lock。在 MySQL 中,当一个线程持有该锁后,其他线程只能不断尝试获取。

mysql> show processlist\G
*************************** 1. row ***************************
     Id: 13239801
   User: root
   Host: localhost
     db: tempdb
Command: Query
   Time: 0
  State: starting
   Info: show processlist
*************************** 2. row ***************************
     Id: 13239868
   User: root
   Host: localhost
     db: tempdb
Command: Query
   Time: 12
  State: Waiting for table metadata lock
   Info: lock tables customer write
2 rows in set (0.00 sec)

1.2 隐式锁

除了显式锁会阻塞这样的操作,MySQL 在查询过程中也会隐式地锁住表。通过 sleep() 函数可以实现长时间的查询,然后 MySQL 会产生一个隐式锁。

在 MySQL 会话中执行 sleep(30),在表 customer上 会获得一个隐式锁。

mysql> select sleep(30) from customer;

在 MySQL 另一个会话中,对表 customer 执行 lock tables 命令,查询会挂起。

mysql> lock tables customer write;

在第三个会话中执行 show processlist 查看线程状态,可以看到线程 13244135 的状态为 Waiting for table metadata lock。select 查询的隐式锁阻塞了 lock tables 中所请求的显式写锁。

mysql> show processlist\G
*************************** 1. row ***************************
     Id: 13244112
   User: root
   Host: localhost
     db: tempdb
Command: Query
   Time: 6
  State: User sleep
   Info: select sleep(30) from customer
*************************** 2. row ***************************
     Id: 13244135
   User: root
   Host: localhost
     db: tempdb
Command: Query
   Time: 2
  State: Waiting for table metadata lock
   Info: lock tables customer write

2. 全局锁

MySQL 服务器可以支持全局读锁,可以通过 flush tables with read lock 或设置 read_only=1 来实现,全局锁与任何表锁都冲突。

在 MySQL会 话中执行 flush tables 命令,获得全局读锁。

mysql> flush tables with read lock;
Query OK, 0 rows affected (0.00 sec)

在 MySQL 另一个会话中,对表 customer 执行 lock tables 命令,查询会挂起。

mysql> lock tables customer write;

在第一个会话中执行 show processlist 查看线程状态,可以看到线程 13283816 的状态为 Waiting for global read lock。这是一个全局读锁,而不是表级别锁。

mysql> show processlist\G
*************************** 1. row ***************************
     Id: 13283789
   User: root
   Host: localhost
     db: tempdb
Command: Query
   Time: 0
  State: starting
   Info: show processlist
*************************** 2. row ***************************
     Id: 13283816
   User: root
   Host: localhost
     db: tempdb
Command: Query
   Time: 10
  State: Waiting for global read lock
   Info: lock tables customer write
2 rows in set (0.00 sec)

3. 命名锁

命名锁是一种表级别锁,它是 MySQL 服务器在重命名或删除表时创建。命名锁与普通的表锁冲突,无论是显式的还是隐式的表锁。

在 MySQL会 话中执行 lock table s命令,在表 customer上 获得一个显式锁。

mysql> lock tables customer read;
Query OK, 0 rows affected (0.00 sec)

在 MySQL 另一个会话中,对表 customer 执行 rename table 命令,此时会话会挂起,会话状态为Waiting for table metadata lock:

mysql> rename table customer to customer_1;

mysql> show processlist\G
...
*************************** 2. row ***************************
     Id: 51
   User: root
   Host: localhost
     db: tempdb
Command: Query
   Time: 128
  State: Waiting for table metadata lock
   Info: rename table customer to customer_1

4. 用户锁

MySQL 服务器还可以实现用户锁,这种锁需指定名称字符串,以及等待超时时间(单位秒)。

在 MySQL 会话中执行 get_lock 命令,成功执行并持有一把锁。

mysql> select get_lock('user_1',20);
+------------------------+
| get_lock('user_1',20) |
+------------------------+
|                      1 |
+------------------------+
1 row in set (0.00 sec)

在 MySQL 另一个会话中,也执行 get_lock 命令,尝试锁相同的字符串,此时会话会挂起,会话状态为User lock。

mysql> select get_lock('user_1',20);
+------------------------+
| get_lock('user_1',20) |
+------------------------+
|                      1 |
+------------------------+

mysql> show processlist\G
...
*************************** 2. row ***************************
     Id: 51
   User: root
   Host: localhost
     db: tempdb
Command: Query
   Time: 3
  State: User lock
   Info: select get_lock('user_1',20)

5. 小结

本小节介绍了服务器级别的锁等待:表锁、全局锁、命名锁、用户锁。

表锁可以是显式的,也可以是隐式的。显式锁通过 lock tables 和 unlock tables 进行控制,隐式锁在查询过程中产生。全局锁可以通过 flush tables with read lock 或设置 read_only=1 来 实现,它与任何表锁都冲突。