mysql多线程update发生死锁

 

问题描述

mysql使用InnoDB引擎,在多线程并发的情况下,发现对数据库表中的数据进行更新操作时发生了死锁

Error updating database. 

Cause: com.mysql.jdbc.exceptions.jdbc4.MySQLTransactionRollbackException: Deadlock found when trying to get lock; try restarting transaction

基础知识

mysql 引擎

1、MyISAM:默认表类型,它是基于传统的ISAM类型,ISAM是Indexed Sequential Access Method (有索引的顺序访问方法) 的缩写,它是存储记录和文件的标准方法。不是事务安全的,而且不支持外键,如果执行大量的select,insert MyISAM比较适合。

2、InnoDB:支持事务安全的引擎,支持外键、行锁、事务是他的最大特点。如果有大量的update和insert,建议使用InnoDB,特别是针对多个并发和QPS较高的情况。

mysql

表级锁:锁住整张表
行级锁:锁住一行或者多行
ps: 如果数据库表没有加索引,那么更新操作的时候会锁住整张表,也就是所谓的表级锁;如果有索引,并且查询条件中也带有这些字段,那么就会使用行级锁。行级锁并不是锁记录,而是锁索引。

误区

根据上面锁的原理,对引擎为InnoDB的数据库表进行更新操作,想当然的以为只要对更新语句中where条件所用到的每个字段加上索引,那么where后面跟着多个条件字段并且理论上能够唯一确定一条记录的情况下,对这一条记录进行更新操作时,只会对这一条记录(记录的索引)加锁,问题就出在这里。

因为行锁是对索引加锁,那么当where语句中包含多个条件时候,mysql在生成执行计划的时候实际上也只用到一个字段的索引(区分度最大的字段),所以即使where语句中包含多个字段,实际上也只使用了一个字段的索引,那么根据这个字段进行过滤出来的记录数可能就不止一条,这个可以通过explain查看到。

如下面更新操作:

explain UPDATE `binlog`.t_binlog_process_log SET status = 0  WHERE database_name = 'loandb'  AND file_partitions = 'year=2018/month=11/day=11'  AND file_name = '1541909458303-bin-log.003368'  AND type = 'create'  AND table_name = 't_user_data_supply_status'  AND status = 99

发现执行计划中虽然列出了所有的索引,但是真正用到的索引是file_name这个字段上的索引,锁住的记录数也就不是一条,而是根据flie_name能够查询出来的记录条数。于是当高并发的情况下,当两个事务同时需要对同一个file_name的记录记性更新操作时,由于其中一个事务把同一个file_name的所有记录都锁住了,那么必然会导致另外一个事务无法获取到锁。

解决方法

即使where条件中所有的字段有添加了索引,真正查询的时候也只能用到其中一个字段的索引,于是想能不能建立一个联合索引,对where条件中所用到的所有的字段共同构建一个联合索引,再次执行explain,发现这个时候确实只锁住了一条记录,问题解决。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值