我有一个innoDB表,记录在线用户。它会在用户每次刷新页面时进行更新,以跟踪他们所在的页面以及他们上次访问该网站的日期。然后我有一个每 15 分钟运行一次的 cron 来删除旧记录。
我收到“尝试获取锁定时发现死锁;”昨晚尝试重新启动事务大约 5 分钟,似乎是在运行 INSERT 到该表时。有人可以建议如何避免此错误吗?
===编辑===
以下是正在运行的查询:
第一次访问网站:
INSERT INTO onlineusers SET
ip = 123.456.789.123,
datetime = now(),
userid = 321,
page = '/thispage',
area = 'thisarea',
type = 3
在每个页面刷新:
UPDATE onlineusers SET
ips = 123.456.789.123,
datetime = now(),
userid = 321,
page = '/thispage',
area = 'thisarea',
type = 3
WHERE id = 888
每 15 分钟执行一次:
DELETE FROM onlineusers WHERE datetime <= now() - INTERVAL 900 SECOND
然后它会进行一些计数以记录一些统计数据(即:在线成员、在线访客)。
可以帮助解决大多数死锁的一个简单技巧是按特定顺序对操作进行排序。
当两个事务试图以相反的顺序锁定两个锁时,就会出现死锁,即:
- 连接1:锁钥匙(1),锁钥匙(2);
- 连接2:锁钥匙(2),锁钥匙(1);
如果两者同时运行,连接 1 将锁定 key(1),连接 2 将锁定 key(2),每个连接将等待对方释放 key -> 死锁。
现在,如果您更改查询,以便连接将以相同的顺序锁定密钥,即:
- 连接1:锁钥匙(1),锁钥匙(2);
- 连接2:锁钥匙(1), 锁钥匙(2);
不可能出现僵局。
所以这就是我的建议:
-
确保除了删除语句之外,没有其他查询一次锁定多个键的访问。如果您这样做(我怀疑您这样做),请按升序对 (k1,k2,..kn) 中的 WHERE 进行排序。
-
修复您的删除语句以按升序工作:
Change
DELETE FROM onlineusers
WHERE datetime <= now() - INTERVAL 900 SECOND
To
DELETE FROM onlineusers
WHERE id IN (
SELECT id FROM onlineusers
WHERE datetime <= now() - INTERVAL 900 SECOND
ORDER BY id
) u;
另一件需要记住的事情是 MySQL 文档建议,如果出现死锁,客户端应该自动重试。您可以将此逻辑添加到您的客户端代码中。 (比如说,在放弃之前重试此特定错误 3 次)。
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)