这部分内容是基于上一节关于死锁的内容而来的Section 14.2.2.8, “Deadlock Detection AND Rollback”,它解释了为了最小化死锁我们应该如何来操作数据库,以及发生错误后如何处理的问题。
在事务性数据库中,死锁是一个典型的的问题,除非他们频繁出现使得你根本无发运行事务,一般情况下没什么危险。一般情况下,你编写的应用程序应该满足当出现死锁回滚的时候,你必须得准备重新执行事务;
InnoDB使用自动行级锁定,即使你只是插入或删除一行数据,也可能会出现死锁。那是因为这些操作并不是真正的原子的,他们自动地在插入或删除行的索引上自动的添加锁;
你可以通过下面的方式来处理死锁或者降低他们发生的可能性:
1.随时执行SHOW ENGINE INNODB STATUS命令来找出最近出现死锁的原因,对调整你的应用来来避免死锁非常有用;
2.如果频繁的出现死锁应该引起你的高度关注,启用innodb_print_all_deadlocks配置选项来获取更多的系统调试信息,这样的话,关于死锁发的信息都会被记录到error log中,不仅仅是最近的一个,调试结束后禁用掉改选项;
3.如果生产环境上不允许由于死锁导致的事务失败,那么你的应用必须有一种机制来保证事务失败回滚后的重新执行,死锁并不可怕,重新执行下回滚事务就行;
4.为了防止冲突应该保证事务小而且尽量缩短执行时间;
5.为了避免冲突细化where条件分解集合的更新,一小组更新后即可提交事务,特别地,不要让一个未提交的事务长时间的处于一个交互式的会话当中;
6.如果你的应用中使用到SELECT ... FOR UPDATE OR SELECT ... LOCK IN SHARE MODE这样的锁定读,那么你可以使用像RC这样的事务隔离级别;
7.如果在一个事务中需要对多个表或者一张表的多个地方做修改,那么每一次做这些操作都按照固定的顺序执行,那么这种类似于队列形式的顺序执行就不会产生死锁。比如,将这些操作打包成一个函数或者存储过程来调用,而不是多个具有相同顺序的insert、update、delete语句;
8.表上的索引尽量添加选择性好的,这样你的查询会扫描更少的索引设置更少的锁,用explain select来检查你的索引建立的是否合适;
9.使用更少的锁定,如果你允许select查询从一个老的快照返回数据,不要在select语句上添加FOR UPDATE或者LOCK IN SHARE MODE子句.使用RC隔离级别是一个不错的主意,因为在同一个事务中,每一个一致性读都是来自于他本身最新的快照。
10.如果还不行的话,使用表锁来使得你的事务串行化,其做法是用SET autocommit=0开启一个事务(不是start transaction)并在其后像InnoDB这样的事务表上加lock tables语句来锁表,在提交你的事务后再执行unlock tables。比如你想写t1表读t2表,那么像下面这样做:
SET autocommit=0;
LOCK TABLES t1 WRITE, t2 READ, ...;
... DO something WITH TABLES t1 AND t2 here ...
COMMIT;
UNLOCK TABLES;
表锁是为了防止同时更新同一张表,避免死锁给系统带来的开销。
11.还有一个办法是创建一个只有单行的副表来串行化事务,让没一个事务在访问表前更新这个行,用这种方式所有的事务都是以串行化的方式执行,注意在这种情况下InnoDB的死锁检测机制会检测死锁,因为串行化锁是行级锁。用MySQL的表锁,会用timeout的方式来解决死锁。
阅读(759) | 评论(0) | 转发(0) |