mysql关于死锁的优化_20.3.9 关于死锁 深入浅出MySQL——数据库开发、优化与管理维护…

  • Post author:
  • Post category:mysql


20.3.9 关于死锁

上文讲过,MyISAM表锁是deadlock free的,这是因为MyISAM总是一次获得所需的全部锁,要么全部满足,要么等待,因此不会出现死锁。但在InnoDB中,除单个SQL组成的事务外,锁是逐步获得的,这就决定了在InnoDB中发生死锁是可能的。如表20-17所示的就是一个发生死锁的例子。

表20-17           InnoDB存储引擎中的死锁例子

session_1

session_2

mysql> set autocommit = 0;

Query OK, 0 rows affected (0.00 sec)

mysql> select * from table_1 where where id=1 for update;

做一些其他处理…

mysql> set autocommit = 0;

Query OK, 0 rows affected (0.00 sec)

mysql> select * from table_2 where id=1 for update;

select * from table_2 where id =1 for update;

因session_2已取得排他锁,等待

做一些其他处理…

mysql> select * from table_1 where where id=1 for update;

死锁

在上面的例子中,两个事务都需要获得对方持有的排他锁才能继续完成事务,这种循环锁等待就是典型的死锁。

发生死锁后,InnoDB一般都能自动检测到,并使一个事务释放锁并回退,另一个事务获得锁,继续完成事务。但在涉及外部锁,或涉及表锁的情况下,InnoDB并不能完全自动检测到死锁,这需要通过设置锁等待超时参数innodb_lock_wait_timeout来解决。需要说明的是,这个参数并不是只用来解决死锁问题,在并发访问比较高的情况下,如果大量事务因无法立即获得所需的锁而挂起,会占用大量计算机资源,造成严重性能问题,甚至拖跨数据库。我们通过设置合适的锁等待超时阈值,可以避免这种情况发生。

通常来说,死锁都是应用设计的问题,通过调整业务流程、数据库对象设计、事务大小,以及访问数据库的SQL语句,绝大部分死锁都可以避免。下面就通过实例来介绍几种避免死锁的常用方法。

(1)在应用中,如果不同的程序会并发存取多个表,应尽量约定以相同的顺序来访问表,这样可以大大降低产生死锁的机会。在下面的例子中,由于两个session访问两个表的顺序不同,发生死锁的机会就非常高!但如果以相同的顺序来访问,死锁就可以避免。

表20-18  InnoDB存储引擎中表顺序造成的死锁例子

session_1

session_2

mysql> set autocommit=0;

Query OK, 0 rows affected (0.00 sec)

mysql> set autocommit=0;

Query OK, 0 rows affected (0.00 sec)

mysql> select first_name,last_name from actor where actor_id = 1 for update;

+————+———–+

| first_name | last_name |

+————+———–+

| PENELOPE| GUINESS|

+————+———–+

1 row in set (0.00 sec)

mysql> insert into country (country_id,country) values(110,’Test’);

Query OK, 1 row affected (0.00 sec)

mysql>insert into country (country_id,country) values(110,’Test’);

等待

mysql> select first_name,last_name from actor where actor_id = 1 for update;

+————+———–+

| first_name | last_name |

+————+———–+

| PENELOPE| GUINESS|

+————+———–+

1 row in set (0.00 sec)

mysql>insert into country (country_id,country) values(110,’Test’);

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

(2)在程序以批量方式处理数据的时候,如果事先对数据排序,保证每个线程按固定的顺序来处理记录,也可以大大降低出现死锁的可能。

表20-19  InnoDB存储引擎中表数据操作顺序不一致造成的死锁例子

session_1

session_2

mysql> set autocommit=0;

Query OK, 0 rows affected (0.00 sec)

mysql> set autocommit=0;

Query OK, 0 rows affected (0.00 sec)

mysql> select first_name,last_name from actor where actor_id = 1 for update;

+————+———–+

| first_name | last_name |

+————+———–+

| PENELOPE| GUINESS|

+————+———–+

1 row in set (0.00 sec)

mysql> select first_name,last_name from actor where actor_id = 3 for update;

+————+———–+

| first_name | last_name |

+————+———–+

| ED| CHASE|

+————+———–+

1 row in set (0.00 sec)

mysql> select first_name,last_name from actor where actor_id = 3 for update;

等待

mysql> select first_name,last_name from actor where actor_id = 1 for update;

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

mysql> select first_name,last_name from actor where actor_id = 3 for update;

+————+———–+

| first_name | last_name |

+————+———–+

| ED| CHASE|

+————+———–+

1 row in set (4.71 sec)



版权声明:本文为weixin_33181210原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。