一个数据库死锁问题

  1. 一【场景

    之前系统在运行过程中,老是报一个诡异的死锁检测异常: Error Code: 1213

    Deadlock found when trying to get lock; try restartingtransaction。最后仔细研究了一下终于解决了。场景模拟如下:

    数据库中2张表:用户表:users,和订单表orders。用户表里面有个字段total用来累计每个用户的订单消费总额,同时orders通过字段user_id与users表做了外键关联。

    表users:


    CREATE TABLE `users` (
      `id` int(11) NOT NULL AUTO_INCREMENT,
      `name` varchar(16) NOT NULL DEFAULT '' COMMENT '会员名',
      `total` decimal(11,2) NOT NULL DEFAULT '0.00' COMMENT '消费总额',
      PRIMARY KEY (`id`)
    ) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8
    

    orders:


    CREATE TABLE `orders` (
      `id` int(11) NOT NULL AUTO_INCREMENT,
      `user_id` int(11) NOT NULL,
      `amount` decimal(11,2) NOT NULL DEFAULT '0.00' COMMENT '订单金额',
      PRIMARY KEY (`id`),
      KEY `USER_ID` (`user_id`),
      CONSTRAINT `USER_ID` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`)
    ) ENGINE=InnoDB AUTO_INCREMENT=17 DEFAULT CHARSET=utf8
    


    假设事务A与事务B按照以下序列进行,就会产生死锁,从而引发数据库的死锁检测异常,MySQL就会选择影响行数较少的事务进行回滚:https://dev.mysql.com/doc/refman/5.5/en/innodb-deadlock-detection.html;

    序列

    事务A

    事务B

    1

    BEGIN;

    INSERT INTO orders (user_id, amount) VALUES (1, 10.00);

     

    2

     

    BEGIN;

    INSERT INTO orders (user_id, amount) VALUES (1, 25.00);

    3

    UPDATE users SET total=total+10.00;(发送阻塞)

     

    4

     

    UPDATE users SET total=total+25.00;(产生死锁)


    二【分析】

    先看死锁日志:


    根据2个事务的WAITING FOR THIS LOCK TO BE GRANTED信息可以看出事务A(D68)和事务B(D69)同时在等着给user表中的同一行加X锁,同时D69事务已经获取了这一行的S锁。那么,这儿的问题是这个共享锁是怎么加上的?

    后来查看了Mysq的官方文档:https://dev.mysql.com/doc/refman/5.6/en/innodb-foreign-key-constraints.html


    就是如果存在外键约束,那么会给这张表的外键关联的表相应的行加上共享锁。那么在我们的这个场景下,就是当insert 2个订单数据的时候,MySQL已经给user表中tom那一行加上了2把共享锁,所以当后面再想着更新tom会员信息的时候,2个事务都在等着对方释放各自的共享锁,于是就产生了死锁。

    三【解决】

    就目前的这个场景,当然是先更新user表,再插入orders表数据就行了。这样就把user表的S锁直接替换成了X锁,破坏了请求和保持的必要条件,预防了死锁的发生。

    不过,现在互联网企业为了方便分表,分库,数据迁移等,已经越来越少的去建立表的外键约束,而是靠上层应用自己去保证了。


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值