2010-11-12 68 views
0

在我的数据库中运行特定查询(所有表都使用InnoDB存储引擎)时,我收到以下错误:“试图锁定时发现死锁;尝试重新启动事务”在MySQL(InnoDB)中发现死锁

查询是DELETE FROM sessions WHERE userid != 0 AND lastactivity < 1289594761 AND admin = 1 AND userid NOT IN (SELECT userid FROM users WHERE (userflags & 1048576))

的错误开始时,我已经添加了非部分我的WHERE语句出现。 为什么这会导致问题,我该怎么做才能防止这种情况发生?

+0

尽管您可能能够最小化死锁,请参阅http://stackoverflow.com/questions/2596005/working-around-mysql-error-deadlock-found-when-trying-to -get-lock-try-restartin – nos 2010-11-12 21:08:46

回答

2

一个简单的解决方案是将它分成两个连续的查询。即:

SELECT userid into #tmptable FROM users WHERE(userflags & 1048576);

DELETE FROM sessions WHERE userid!= 0 AND lastactivity < 1289594761 AND admin = 1 AND userid NOT IN(select userid from #tmptable);

这样你就可以使用第二个表中的值的本地会话副本并且不会对其造成读锁定。但是,这只是一个快速而肮脏的解决方案。一个更好的解决方案是分析所有触及这两个表的活动的事务锁设置,并重新编写查询,如果您定期重新使用它。

+0

它只用于维护cronjob,所以如果它不时失败,这不是一件大事情 - 我已经忽略了他们几个月,但有删除错误通知邮件变得烦人。 ;) – ThiefMaster 2010-11-12 21:17:15

+0

顺便说一句,我已经做了类似于你的建议 - 不使用临时表,而是简单地将用户ID列表存储在我的PHP代码中,然后在DELETE语句中使用'IN(list,of,userids)' 。 – ThiefMaster 2010-11-12 21:23:17

2

大概你会更频繁地得到错误,因为这是一个非常慢的查询。

& op userflags使子查询无法索引。标记词通常不是很好的模式设计,因为它们需要计算失败索引。如果您正在进行大量测试查询,那么将小数据类型的单独列(例如TINYINT)可能会更好。

如果你的模式工作,它看起来像它可能的方式,你应该能够做到这一点使用一个简单连接,通常执行比子查询较好:

DELETE sessions 
FROM sessions 
JOIN users ON users.userid=sessions.userid 
WHERE sessions.lastactivity<1289594761 AND admin=1 
AND (users.userflags&1048576)=0 

(上DELETE加入是一个非-ANSI SQL扩展在MySQL中。)

+0

我猜你想说AND NOT(users.userflags&1048579)' - 但不错的建议,我没有考虑使用JOIN,因为IMO在DELETE/UPDATE语句中使用JOIN使得查询看起来很奇怪 – ThiefMaster 2010-11-12 21:29:57

+0

啊,是的,你是正确的。 – bobince 2010-11-12 21:35:46