MySQL InnoDB 共享读锁与排他写锁(S Lock and X Lock)

内容纲要

1. MySQL InnoDB锁列表

在此,我将对InnoDB存储引擎中的锁的类型和用法进行详细说明。InnoDB提供了多种类型的锁,例如,共享锁(S锁),排他锁(X锁),意向共享锁(IS锁)等,并且还具有一些特殊锁类型,”。本文主要讨论和示例实验共享与排他锁的使用。

以下是InnoDB存储引擎中的锁类型:

  • 共享锁(S 锁): 允许一个事务去读一行,阻止其他事务获得相同数据集的排他锁。
  • 排他锁(X 锁): 允许获得排他锁的事务更新数据,阻止其他事务取得相同数据集的共享读锁和排他写锁。
  • 意向共享锁(IS 锁): 表明一个事务打算在表中的行上设置 S 锁。
  • 意向排他锁(IX 锁): 表明一个事务打算在表中的行上设置 X 锁。
  • 记录锁: 用于锁定表中的行。
  • 间隙锁: 用于锁定表中的一个“间隙”,用以防止并发的插入操作。
  • Next-Key锁: 锁定一个索引记录与该记录之前的“间隙”,即锁定的是该键值的前闭后开区间。
  • 插入意向锁: 当InnoDB插入一行时,它会先在被插入的位置设置一个插入意向锁,这意味着这个事务打算插入一行。
  • AUTO-INC锁: 在插入AUTO_INCREMENT列的新值时使用。

2. 共享与排他锁

接下来,让我们详细看一下共享与排他锁,这两种是最基本且最常用的锁。
共享与排他锁也可称为共享读锁和排他写锁,分别用大写字母S和X表示。S和X分别对应shared(共享) locks 和exclusive(排他) locks。

  • 共享锁(S 锁) – 允许事务持有进行读行。多个事务可以同时持有共享锁。
  • 排他锁(X 锁) – 允许事务持有进行更新或删除行且只有一个事务可以持有排他锁。

如果一个事务持有排他锁,那么其他事务只能等待持有该锁的事务释放锁,如果事务等待太久未得到锁,等待的事务可能会发生回滚。

以下是一个示例,两个事务同时对同一行进行数据更新:
首先,启动两个可以对同一行数据进行更新的事务。
例如,如下列SQL命令所示,"begin;"命令表示开始一个新的事务,而"update sys_user set name_pinyin=’wangwu1′ where id=17;"命令表示将表sys_user中id为17的行的姓名拼音字段更新为’wangwu1’。在这两个事务中,哪个先执行,就会优先获得锁。

-- 事务A
begin;
update sys_user set name_pinyin='wangwu1' where id=17;

-- 事务B
begin;
update sys_user set name_pinyin='wangwu1' where id=17;

如果事务A先执行update语句,那么事务A会先获取到id=17行的排他写锁,此时如果事务B执行update语句,事务B只能等待事务A释放锁。如果事务A没有在事务B等待超时之前释放锁的话,那么事务B就会回滚。

当事务B等待锁的过程中,我们可以使用以下列出的SQL语句来查询当前持有锁的事务和等待锁的事务的情况。

SELECT r.trx_id AS waiting_trx_id, r.trx_mysql_thread_id AS waiting_thread, LEFT(r.trx_query, 20) AS waiting_query
 , CONCAT(lw.lock_type, ' ', lw.lock_mode) AS waiting_for_lock
 , b.trx_id AS blocking_trx_id, b.trx_mysql_thread_id AS blocking_thread, LEFT(b.trx_query, 20) AS blocking_query
 , CONCAT(lb.lock_type, ' ', lb.lock_mode) AS blocking_lock
FROM performance_schema.data_lock_waits w
 INNER JOIN information_schema.innodb_trx b ON b.trx_id = w.BLOCKING_ENGINE_TRANSACTION_ID
 INNER JOIN information_schema.innodb_trx r ON r.trx_id = w.REQUESTING_ENGINE_TRANSACTION_ID
 INNER JOIN performance_schema.data_locks lw ON lw.ENGINE_TRANSACTION_ID = r.trx_id
 INNER JOIN performance_schema.data_locks lb ON lb.ENGINE_TRANSACTION_ID = b.trx_id;

这将显示等待锁的事务及其请求,以及导致锁定的事务及其锁定类型。

3. 注意

  • 在可以重复读的事务隔离级别下,InnoDB并不会为SELECT语句自动加上共享锁,而是采用了另一种机制,即多版本并发控制(MVCC)来保证事务的隔离性。然而,这并不意味着SELECT语句不能加锁。在事务中,你可以通过在SELECT语句后面 adding "FOR UPDATE"或"LOCK IN SHARE MODE"来为其添加排他锁或共享锁,称为显式锁定。以下是两种可能的情况:
  1. 显式锁定:即使在可重复读的事务隔离级别下,你仍然可以通过在SELECT语句后面添加FOR UPDATELOCK IN SHARE MODE来显示地为SELECT语句加上排他锁或共享锁。
SELECT * FROM table_name WHERE id = 1 FOR UPDATE;   -- 加排他锁
SELECT * FROM table_name WHERE id = 1 LOCK IN SHARE MODE;  -- 加共享锁
  1. 使用了锁定读:在执行类似于SELECT ... FOR UPDATESELECT ... LOCK IN SHARE MODE的查询时,InnoDB会对涉及的行添加共享锁或排他锁。

所以说"InnoDB对SELECT是不加锁的"这个表述并不完全准确,实际情况取决于你的SQL语句和使用的隔离级别。

4. 总结

以上就是对InnoDB的S和X锁的详细介绍,包括它们的基本概念,用法示例和如何查询锁信息的方式。请记住,正确使用和理解数据库锁非常重要,因为它可以显著影响数据库操作的性能和并发性。希望本文能帮助你在实践中更好地应用这些知识。

5. 参考

  1. MySQL 8.0 Reference Manual: InnoDB Locking

发表评论

您的电子邮箱地址不会被公开。 必填项已用 * 标注

滚动至顶部