【问题标题】:Do MySQL transactions for INSERT lock foreign key referenced tables?INSERT 的 MySQL 事务是否会锁定外键引用的表?
【发布时间】:2019-07-07 10:20:36
【问题描述】:

我正在尝试在我的 Java 应用程序中进行大量事务,并为 user_account_entry 表执行单个插入条目(以千为单位),该表具有对 user 表的外键引用。

当事务运行时,我无法更新属于获取LockAcquisitionException的事务的任何用户实体

我正在使用 MySQL InnoDB 并使用 DEFAULT 隔离级别进行事务处理,该级别转换为 InnoDB 的 REPEATABLE-READ 级别,任何人都可以了解 mysql 事务期间的外键锁定

【问题讨论】:

  • MySQL 使用共享模式(只读)锁锁定所有相关(通过外键)行以确保一致性。要阻止这种情况,您需要删除外键或禁用它们。
  • 希望您使用的是最新版本的 MySQL 并使用 InnoDB。性能在 8.0 中有了很大提高(也有新的锁定选项!),并且 MyIsam 不是为并发插入而设计的
  • 感谢@Vatev,您是否有任何相关文档的链接
  • dev.mysql.com/doc/refman/5.7/en/… 您可以在此页面上找到参考“InnoDB 在它必须查看的子记录或父记录上设置共享行级锁”

标签: mysql transactions innodb isolation-level


【解决方案1】:

是的。

演示:在一个窗口中,创建父子表。

mysql1> create table parent (id int primary key, x int );
Query OK, 0 rows affected (0.04 sec)

mysql1> create table child (id int primary key, parentid int,
    foreign key(parentid) references parent(id));
Query OK, 0 rows affected (0.03 sec)

在父表中插入一行:

mysql1> insert into parent values (1, 1);
Query OK, 1 row affected (0.02 sec)

启动事务并向子表添加一行,引用父行:

mysql1> begin;
Query OK, 0 rows affected (0.00 sec)

mysql1> insert into child values (42, 1);
Query OK, 1 row affected (0.00 sec)

打开第二个窗口,并尝试更新父项中引用的行:

mysql2> update parent set x = 2 where x = 1;

它挂起,等待第一个会话持有的锁。

回到第一个窗口并提交事务,这会释放锁:

mysql1> commit;
Query OK, 0 rows affected (0.02 sec)

在第二个窗口中,更新继续进行,时间显示它等待了将近六秒钟,这是我回到第一个窗口提交所用的时间。

Query OK, 1 row affected (5.92 sec)
Rows matched: 1  Changed: 1  Warnings: 0

【讨论】:

  • 谢谢@Bill 我昨天尝试了同样的事情,得到了同样的结果
  • 在这种情况下,在提交之前,如果会话 1 对同一父行 (update parent set x = 3 where x =1) 执行了自己的更新语句,为什么这会以会话 2 回滚的死锁结束?在会话 1 提交之前,会话 2 不会继续阻塞吗?此外,如果会话 1 首先执行 UPDATE 语句,然后是会话 2 自己的 UPDATE 语句,然后由于某种原因在会话 1 中执行另一个 UPDATE 语句,为什么不再发生死锁而会话 2 最终阻塞?我觉得我在这里缺少一些基本的东西
  • @georaldc 死锁是循环锁等待。会话 2 正在等待会话 1 持有的锁,因此会话 1 必须提交以解除对会话 2 的阻塞。然后会话 1 尝试更新需要会话 2 已在队列中获取的锁的内容。因此会话 1 必须等待。他们都在等着对方。 MySQL 立即注意到这一点(有一个服务器线程在监视这种情况),并强制一个线程回滚它的事务,释放它的锁。
  • 在您的第二个示例中,如果会话 1 首先获取锁,它会保留这些锁,并且可以在提交之前尽可能多次地重复使用它们,即使另一个会话正在等待。一旦获得锁,会话在其事务结束之前不会放弃这些锁。
  • @BillKarwin 不确定这是否回答了我的问题(抱歉,如果我在 cmets 中问这个问题)。我只是想知道如果会话 2 想要做的只是尝试更新特定行,那么它首先持有哪些锁会导致死锁。我刚刚发现有人在这里提出了与我的问题非常相似的问题 (stackoverflow.com/questions/41015813/…),并且接受的答案是这是一个 mysql 怪癖/错误/边缘案例?
【解决方案2】:

Java 具有“批量”插入功能。使用它一次最多可以插入 100 行。这将运行 10 倍的速度,从而有助于减少各种问题不那么频繁

【讨论】:

  • 批量插入如何帮助表锁定和事务?问题与性能无关。请解释一下,我会把它标记为有用的。
  • @otocon - 例如,如果没有其他线程在运行,死锁是不可能的。批处理将使那个线程采取行动的频率大大减少。另一方面,当它运行时,它会运行有点的时间。也就是说,死锁(等)的风险较小(但未消除)。
猜你喜欢
  • 2016-09-16
  • 1970-01-01
  • 2016-03-22
  • 2011-05-12
  • 2019-05-19
  • 1970-01-01
  • 1970-01-01
  • 2018-07-05
  • 1970-01-01
相关资源
最近更新 更多