ACID 事务 innodb 中的行锁定

tom*_*mmo 2 mysql innodb acid

我有一个看起来像这样的金融交易(伪):

begin
    select record for update
    update record(same record as selected)
    insert another record into another table
commit
Run Code Online (Sandbox Code Playgroud)

如果我SELECT ... FOR UPDATE在事务中调用,是否只有在提交事务时才会释放特定行的锁?

Rol*_*DBA 5

根据MySQL 5.0 认证学习指南的第 418 页第 3 段

MySQL 5.0 认证学习指南

以下命令可以中断事务

  • ALTER TABLE
  • BEGIN
  • CREATE INDEX
  • DROP DATABASE
  • DROP INDEX
  • DROP TABLE
  • RENAME TABLE
  • TRUNCATE TABLE
  • LOCK TABLES
  • UNLOCK TABLES
  • SET AUTOCOMMIT = 1
  • START TRANSACTION

只要您在交易中没有执行任何这些命令,交易就应该是稳定的。事实上,SELECT FOR UPDATE 非常强大,以至于行锁会侵入索引和数据。它甚至可能引发 InnoDB 死锁。我曾经在这篇文章中写过:选择更新会在索引列上出现错误

只要你保持交易简短而甜蜜,SELECT FOR UPDATE应该不是问题。如果您想保护您的事务免受间歇性或意外死锁的影响,MySQL 5.6 现在具有以下功能:

  • START TRANSACTION READ WRITE;
  • START TRANSACTION READ ONLY;

您应该使用这些选项开始交易。为什么 ?

根据那个 MySQL 文档

READ WRITE 和 READ ONLY 修饰符设置事务访问模式。它们允许或禁止更改事务中使用的表。READ ONLY 限制防止事务修改或锁定其他事务可见的事务表和非事务表;事务仍然可以修改或锁定临时表。这些修饰符从 MySQL 5.6.5 开始可用。

你的最后一条评论是

那么我可以以另一种方式锁定事务中的行吗?

您还可以运行SELECT ... LOCK IN SHARE MODE。文档说

  • SELECT ... LOCK IN SHARE MODE 在读取的任何行上设置共享模式锁。其他会话可以读取这些行,但在您的事务提交之前无法修改它们。如果这些行中的任何一行被另一个尚未提交的事务更改,您的查询将等到该事务结束,然后使用最新值。
  • 对于搜索遇到的索引记录,SELECT ... FOR UPDATE 锁定行和任何关联的索引条目,就像您为这些行发出 UPDATE 语句一样。其他事务被阻止更新这些行、执行 SELECT ... LOCK IN SHARE MODE 或读取某些事务隔离级别中的数据。一致读取忽略对读取视图中存在的记录设置的任何锁定。(旧版本的记录不能被锁定;它们是通过在记录的内存副本上应用撤消日志来重建的。)

尽管有可能,但尽量缩短交易时间。否则,您可能会增加死锁的可能性。

更新 2014-02-13 21:57 美国东部时间

要清除任何误解,请注意 MySQL 文档中的这一点

当事务提交或回滚时,所有由LOCK IN SHARE MODEFOR UPDATE查询设置的都会被释放。

因此,如果您在事务内执行SELECT ... LOCK IN SHARE MODE,行将保持锁定状态,直到事务提交或回滚。