Causes and Solutions of MYSQL Database Deadlock

Classic and common deadlock causes and solutions when Mysql is concurrent

Summarize MySQL deadlock problems encountered online

 

Like the operating system, the database is a shared resource used by multiple users. When multiple users access data concurrently, there will be multiple transactions accessing the same data at the same time in the database. Without control over concurrent operations, incorrect data may be read and stored, breaking database consistency. Locking is a very important technology for implementing database concurrency control. Lock-related exceptions are often encountered in practical applications. When two transactions require a set of conflicting locks and cannot continue the transaction, a deadlock will occur, which seriously affects the normal execution of the application.

 

There are two basic types of locks in the database: exclusive locks (Exclusive Locks, namely X locks) and shared locks (Share Locks, namely S locks). When an exclusive lock is added to a data object, other transactions cannot read or modify it. Data objects with shared locks can be read by other transactions, but cannot be modified. The database uses these two basic lock types to control the concurrency of database transactions.

 

The first case of deadlock

 

A user A accesses table A (locks table A), and then accesses table B; another user B accesses table B (locks table B), and then attempts to access table A ; at this time, user A is locked because user B To live in table B, it must wait for user B to release table B before it can continue. Similarly, user B has to wait for user A to release table A before it can continue, which leads to a deadlock.

 

Solution:

 

This kind of deadlock is relatively common and is caused by bugs in the program. There is no other way except to adjust the logic of the program . Carefully analyze the logic of the program. When operating on multiple tables in the database, try to process them in the same order , and try to avoid locking two resources at the same time. For example, when operating two tables, A and B are always processed in the order of A first and then B. , when two resources must be locked at the same time, ensure that the resources should be locked in the same order at all times.

 

Second case of deadlock

 

用户A查询一条纪录,然后修改该条纪录;这时用户B修改该条纪录,这时用户A的事务里锁的性质由查询的共享锁企图上升到独占锁,而用户B里的独占锁由于A 有共享锁存在所以必须等A释放掉共享锁,而A由于B的独占锁而无法上升的独占锁也就不可能释放共享锁,于是出现了死锁。这种死锁比较隐蔽,但在稍大点的项 目中经常发生。如在某项目中,页面上的按钮点击后,没有使按钮立刻失效,使得用户会多次快速点击同一按钮,这样同一段代码对数据库同一条记录进行多次操 作,很容易就出现这种死锁的情况。

 

解决方法:

 

1、对于按钮等控件,点击后使其立刻失效,不让用户重复点击,避免对同时对同一条记录操作。

2、使用乐观锁进行控制。乐观锁大多是基于数据版本(Version)记录机制实现。即为数据增加一个版本标识,在基于数据库表的版本解决方案中,一般是 通过为数据库表增加一个“version”字段来实现。读取出数据时,将此版本号一同读出,之后更新时,对此版本号加一。此时,将提交数据的版本数据与数 据库表对应记录的当前版本信息进行比对,如果提交的数据版本号大于数据库表当前版本号,则予以更新,否则认为是过期数据。乐观锁机制避免了长事务中的数据 库加锁开销(用户A和用户B操作过程中,都没有对数据库数据加锁),大大提升了大并发量下的系统整体性能表现。Hibernate 在其数据访问引擎中内置了乐观锁实现。需要注意的是,由于乐观锁机制是在我们的系统中实现,来自外部系统的用户更新操作不受我们系统的控制,因此可能会造 成脏数据被更新到数据库中。

3、使用悲观锁进行控制。悲观锁大多数情况下依靠数据库的锁机制实现,如Oracle的Select … for update语句,以保证操作最大程度的独占性。但随之而来的就是数据库性能的大量开销,特别是对长事务而言,这样的开销往往无法承受。如一个金融系统, 当某个操作员读取用户的数据,并在读出的用户数据的基础上进行修改时(如更改用户账户余额),如果采用悲观锁机制,也就意味着整个操作过程中(从操作员读 出数据、开始修改直至提交修改结果的全过程,甚至还包括操作员中途去煮咖啡的时间),数据库记录始终处于加锁状态,可以想见,如果面对成百上千个并发,这 样的情况将导致灾难性的后果。所以,采用悲观锁进行控制时一定要考虑清楚。

 

死锁的第三种情况

 

如果在事务中执行了一条不满足条件的update语句,则执行全表扫描,把行级锁上升为表级锁,多个这样的事务执行后,就很容易产生死锁和阻塞。类似的情 况还有当表中的数据量非常庞大而索引建的过少或不合适的时候,使得经常发生全表扫描,最终应用系统会越来越慢,最终发生阻塞或死锁。

 

解决方法:

 

SQL语句中不要使用太复杂的关联多表的查询;使用“执行计划”对SQL语句进行分析,对于有全表扫描的SQL语句,建立相应的索引进行优化。

 

 

案例:

 

在开发中,经常会做这类的判断需求:根据字段值查询(有索引),如果不存在,则插入;否则更新。

 

以id为主键为例,目前还没有id=22的行

Session1:
select * from t3 where id=22 for update;

Empty set (0.00 sec)
 

session2:
select * from t3 where id=23  for update;

Empty set (0.00 sec)
 

Session1:
insert into t3 values(22,'ac','a',now());
锁等待中……
 
Session2:
insert into t3 values(23,'bc','b',now());

ERROR 1213 (40001): Deadlock found when trying to get lock; try restarting transaction

 

对存在的行进行锁的时候(主键),mysql就只有行锁。

对不存在的行进行锁的时候(即使条件为主键),mysql是会锁住一段范围(有gap锁)

 

锁住的范围为:

(无穷小或小于表中锁住id的最大值,无穷大或大于表中锁住id的最小值)

 

如:如果表中目前有已有的id为(11 , 12)

那么就锁住(12,无穷大)

 

如果表中目前已有的id为(11 , 30)

那么就锁住(11,30)

 

对于这种死锁的解决办法是:

 

insert into t3(xx,xx) on duplicate key update `xx`='XX';

 

 

用mysql特有的语法来解决此问题。因为insert语句对于主键来说,插入的行不管有没有存在,都会只有行锁。

 

5.小结

总体上来说,产生内存溢出与锁表都是由于代码写的不好造成的,因此提高代码的质量是最根本的解决办法。有的人认为先把功能实现,有BUG时再在测试阶段进 行修正,这种想法是错误的。正如一件产品的质量是在生产制造的过程中决定的,而不是质量检测时决定的,软件的质量在设计与编码阶段就已经决定了,测试只是 对软件质量的一个验证,因为测试不可能找出软件中所有的BUG。

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=325993974&siteId=291194637