带有选择死锁的嵌套更新

2024-02-26

背景

我正在使用一些代码,似乎deadlock经常与自己在一起。在Java中,它产生一个DeadLockLoserDataAccessException定期地,并且导致死锁的违规语句通常是itself。 (这是在 InnoDB 的事务中运行的)

UPDATE a
SET
    a_field = (SELECT sum(b_field) FROM b WHERE b.a_id = a.id)
WHERE 
    a = ?

读了一些书后,我发现了FOR UPDATE执行锁定读取的子句。所以我修改了下面的代码

UPDATE a
SET
    a_field = (SELECT sum(b_field) FROM b WHERE b.a_id = a.id FOR UPDATE)
WHERE 
    a = ?

Question

添加是否合适FOR UPDATE锁定在嵌套内UPDATE/SELECT?上面的例子都没有锁定读取文档 http://dev.mysql.com/doc/refman/5.0/en/innodb-locking-reads.html use FOR UPDATE这样。

表的结构

下面是一个简化版本,其中字段仅适用于查询

Table A

id      int(11) PRIMARY KEY
a_field int(11)

Table B

id      int(11) PRIMARY KEY
a_id    int(11) FOREIGN KEY REFERENCES (a.id)
b_field int(11)

Indexes

唯一存在的索引是单列表 a 的两个主键和外键上的索引。


对你的问题的简单回答是:

是的,MySql 支持FOR UPDATE子查询中的子句

但是,这肯定不能解决您的问题。
在这种情况下,子查询中的 FOR UPDATE 不能防止死锁

由于您没有向我们展示整个事务,而只向我们展示了一个片段,因此我猜测事务中一定有其他命令在外键引用的记录上放置了锁。

为了更好地理解 MySql 中的锁定是如何工作的,请看一下这个简单的示例:

CREATE TABLE `a` ( 
   `id` int(11) primary key AUTO_INCREMENT, 
   `a_field` int(11) 
);
CREATE TABLE `b` ( 
   `id` int(11) primary key AUTO_INCREMENT, 
   `a_id` int(11), 
   `b_field` int(11),
   CONSTRAINT `b_fk_aid` FOREIGN KEY (`a_id`) REFERENCES `a` (`id`)
);
CREATE TABLE `c` ( 
   `id` int(11) primary key AUTO_INCREMENT, 
   `a_id` int(11), 
   `c_field` int(11),
   CONSTRAINT `c_fk_aid` FOREIGN KEY (`a_id`) REFERENCES `a` (`id`)
);

insert into a( a_field ) values ( 10 ), ( 20 );
insert into b( a_id, b_field ) values ( 1, 20 ), ( 2, 30 );

delimiter $$
create procedure test( p_a_id int, p_count int )
begin
   declare i int;
   set i = 0;
   REPEAT
      START TRANSACTION;
      INSERT INTO c( a_id, c_field ) values ( p_a_id, round(rand() * 100) );
      UPDATE a
         SET  a_field = (
                   SELECT sum(b_field) 
                   FROM b WHERE b.a_id = a.id 
                   FOR UPDATE)
         WHERE 
                id = p_a_id;
       commit; 
       set i = i + 1;
   until i > p_count 
   end repeat;
end $$
DELIMITER ;

请注意FOR UPDATE用于子查询中。
如果我们同时在两个会话中执行该过程:

call test( 2, 400 );

我们几乎立刻就得到了一个死锁错误:

------------------------
LATEST DETECTED DEADLOCK
------------------------
2013-09-05 23:08:27 1b8c
*** (1) TRANSACTION:
TRANSACTION 1388056, ACTIVE 0 sec starting index read, thread declared inside InnoDB 5000
mysql tables in use 2, locked 2
LOCK WAIT 5 lock struct(s), heap size 1248, 2 row lock(s), undo log entries 1
MySQL thread id 6, OS thread handle 0x1db0, query id 3107246 localhost 127.0.0.1 test updating
UPDATE a
         SET  a_field = (
                   SELECT sum(b_field) 
                   FROM b WHERE b.a_id = a.id 
                   FOR UPDATE)
         WHERE 
                id = p_a_id
*** (1) WAITING FOR THIS LOCK TO BE GRANTED:
RECORD LOCKS space id 222 page no 3 n bits 72 index `PRIMARY` of table `test`.`a` trx id 1388056 lock_mode X locks rec but not gap waiting
Record lock, heap no 3 PHYSICAL RECORD: n_fields 4; compact format; info bits 0
 0: len 4; hex 80000002; asc     ;;
 1: len 6; hex 000000152e16; asc     . ;;
 2: len 7; hex 2d0000013b285a; asc -   ;(Z;;
 3: len 4; hex 8000001e; asc     ;;

*** (2) TRANSACTION:
TRANSACTION 1388057, ACTIVE 0 sec starting index read, thread declared inside InnoDB 5000
mysql tables in use 2, locked 2
5 lock struct(s), heap size 1248, 2 row lock(s), undo log entries 1
MySQL thread id 7, OS thread handle 0x1b8c, query id 3107247 localhost 127.0.0.1 test updating
UPDATE a
         SET  a_field = (
                   SELECT sum(b_field) 
                   FROM b WHERE b.a_id = a.id 
                   FOR UPDATE)
         WHERE 
                id = p_a_id
*** (2) HOLDS THE LOCK(S):
RECORD LOCKS space id 222 page no 3 n bits 72 index `PRIMARY` of table `test`.`a` trx id 1388057 lock mode S locks rec but not gap
Record lock, heap no 3 PHYSICAL RECORD: n_fields 4; compact format; info bits 0
 0: len 4; hex 80000002; asc     ;;
 1: len 6; hex 000000152e16; asc     . ;;
 2: len 7; hex 2d0000013b285a; asc -   ;(Z;;
 3: len 4; hex 8000001e; asc     ;;

*** (2) WAITING FOR THIS LOCK TO BE GRANTED:
RECORD LOCKS space id 222 page no 3 n bits 72 index `PRIMARY` of table `test`.`a` trx id 1388057 lock_mode X locks rec but not gap waiting
Record lock, heap no 3 PHYSICAL RECORD: n_fields 4; compact format; info bits 0
 0: len 4; hex 80000002; asc     ;;
 1: len 6; hex 000000152e16; asc     . ;;
 2: len 7; hex 2d0000013b285a; asc -   ;(Z;;
 3: len 4; hex 8000001e; asc     ;;

*** WE ROLL BACK TRANSACTION (2)
------------

正如您所看到的,MySql 报告死锁错误是由相同的两个 UPDAT 引起的。

然而,这只是事实的一半。

死锁错误的真正原因是INSERT INTO c语句,该语句在 A 表中的引用记录上放置了共享锁(因为 A 表中的 FOREIGN KEY 约束)C table).

并且 - 令人惊讶的是 - 为了防止死锁,必须在行上放置一个锁A事务开始时的表:

  declare dummy int;
  ...... 
  START TRANSACTION;
      SELECT id INTO dummy FROM A 
      WHERE id = p_a_id FOR UPDATE;
      INSERT INTO c( a_id, c_field ) values ( p_a_id, round(rand() * 100) );
      UPDATE a
         SET  a_field = (
                   SELECT sum(b_field) 
                   FROM b WHERE b.a_id = a.id 
              )
         WHERE 
                id = p_a_id;
       commit; 

进行此更改后,过程运行不会出现死锁。

因此,您可以尝试添加SELECT ... FROM A ... FOR UPDATE在您的交易开始时。

但如果这不起作用,要获得进一步帮助来解决此问题,请:

  • 显示整个事务(事务中涉及的所有命令)
  • 显示事务使用的所有表的结构
  • 显示在插入/更新/删除时触发的触发器,这些触发器修改事务涉及的表
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

带有选择死锁的嵌套更新 的相关文章

  • 通过 JDBC 与 CLI 使用 MIT Kerberos 连接到 PostgreSQL 9.4 时出错

    我已经使用 MIT Kerberos 5 设置了 PostgreSQL 9 4 并且可以使用 psql 在 CLI 上连接 提交指纹后 我的委托人是 bgiles postgres REALM pg hba conf 有 host all
  • Java - 了解 PrintWriter 和刷新的需要

    好吧 首先我对所有代码表示歉意 但我觉得代码太多总比代码不够好 我正在制作一个简单的聊天客户端和印刷机 尤其是我正在努力解决的问题 使用现在的代码 它将与服务器类交互 并且完美地打印我想要打印的内容 但是 当我删除 writer flush
  • 为什么我们要关闭 Mysqli 中的结果

    为什么我们要关闭 result mysqli new mysqli localhost root root test if mysqli gt connect errno echo Failed to connect to MySQL my
  • BigDecimal 中 Divide 方法的 Scale()

    new BigDecimal 37146555 53880000 divide new BigDecimal 1000000 scale 这返回10 但根据API divide method 返回一个 BigDecimal 其值为 这个 除
  • java模拟自定义对象

    public class MainClass public void makeCall CustomObject obj new CustomObject obj testMethod 我想进行单元测试makeCall 所以我必须嘲笑Cus
  • 在Tomcat中设置环境变量TESSDATA_PREFIX

    我们正在使用名为 Tess4J 的 Tesseract OCR Java 库 如果作为独立应用程序运行 它可以正常工作 它需要一个名为 TESSDATA PREFIX 的变量 其中包含 tessdata 配置和其他字符集相关文件 它也可以与
  • MySQL更改表,添加具有唯一随机值的列

    我有一个表 我添加了一个名为phone 该表还有一个 id 设置为自动增量的主键 如何将随机值插入到电话列中 该值不会重复 以下 UPDATE 语句确实插入了随机值 但并非所有值都是唯一的 另外 我没有被卖掉 我投了phone字段也正确 但
  • sqlalchemy 中的随机 ID(pylon)

    我正在使用 pylons 和 sqlalchemy 我想知道如何将一些随机 id 作为primary key 最好的方法是使用随机生成的 UUID import uuid id uuid uuid4 uuid 数据类型在某些数据库中本机可用
  • 大型 XML 的 XML 节点到字符串转换

    到目前为止我一直在使用DOM源在我的 Android 应用程序中将 XML 文件转换为字符串 这是我的代码 public String convertElementToString Node element throws Transform
  • JavaFX 动画使用循环?

    我正在尝试制作一款类似太空侵略者的游戏 我画了一个正方形 我想通过使用循环逐步向下移动它thread sleep 然而 正方形立即被绘制出来 我知道有可以使用的动画路径 但我想保持低水平并仅使用坐标系 有没有办法使用这样的循环来制作时间轴动
  • 酷还是傻? Catch(异常[NamingException, CreateException] e)

    我正在编写一些代码 我注意到异常处理中的一种模式让我思考 try do stuff throws JMS Create and NamingException catch NamingException e log1 e rollback
  • 如何在开头时解析 json 文件

    我想解析以下 JSON 文件 但以 向我表明这是一个数组 然后继续 对象 我当前的解析器返回一个 JSON 对象 我的问题是 如何修改解析器来解析这个文件 这样解析器将为我提供其他 JSON 文件 从对象或排列开始 JSON 文件 codi
  • Java Timer 类:如果其中一个任务抛出异常,则计时器任务停止执行

    new Timer scheduleAtFixedRate new TimerTask Override public void run System out println run throw new SomeRandomExceptio
  • 在 Java/GWT 中解析用户时间输入

    解析用户在 GWT 中的文本字段中键入的时间的最佳方法是什么 默认时间格式要求用户完全按照区域设置指定的时间格式输入时间 我想要更加灵活 因为用户可以通过多种不同的方式输入时间 例如 8 8p 8pm 8 15pm 13 15 1315 1
  • 为什么 Cassandra 客户端在生产中没有 epoll 时会失败? [复制]

    这个问题在这里已经有答案了 当我在本地运行服务时 我收到一条警告 指出 epoll 不可用 因此它使用 NIO 很公平 当我将其部署到 Kubernetes 中时 我得到了以下信息 这导致服务无法运行 2017 03 29T19 09 22
  • 使用泛型进行选择排序

    我对整数进行了选择排序并且它正在工作 当我尝试修改程序以使用泛型时 编译器会抱怨 我不知道如何修复它 如果有人能提出一些建议和建设性意见 我将不胜感激 这是代码 public class SelelctionSort public stat
  • 飞碟 - html 实体未呈现

    我正在使用 Flying saucer lib 生成 pdf 但我对一些 html 实体有问题 我已经在寻找解决方案 我在这个论坛和其他地方找到了很多提示 但仍然存在问题 我尝试过这种方法 http sdtidbits blogspot c
  • 在旧版本的 MySQL (<5.5.0) 中模拟 TO_SECONDS()

    出于性能和简单性的原因 我想以秒的形式获取 MySQL 3 x 服务器中 DATETIME 列的内容 或者实际上任何数字类型 我只是想在使用 UNIX TIMESTAMP 时避免所有明显的时区问题 the我表中的日期确实来自不同的区域设置
  • 如何使用 Kafka 发送大消息(超过 15MB)?

    我发送字符串消息到Kafka V 0 8使用 Java Producer API 如果消息大小约为 15 MB 我会得到MessageSizeTooLargeException 我尝试过设置message max bytes到 40 MB
  • 使用 Spring Batch 将文件中的日期解析为 LocalDateTime

    我正在尝试使用 Spring Batch 读取包含日期的 CSV 文件 但在将日期解析为LocalDateTime Object 字段 日期 上的对象 目标 中的字段错误 拒绝值 2017 07 20 04 15 25 0 代码 typeM

随机推荐