我有一个记录在线用户的innoDB表.它会在用户每次刷新页面时更新,以跟踪他们所在的页面以及他们最后一次访问网站的日期.然后我有一个cron,每15分钟运行一次,删除旧记录.

我在试图打开锁时遇到了"死锁";昨晚try 重新启动"事务"大约5分钟,在向该表中运行INSERT时似乎是这样.有人能建议如何避免这个错误吗?

==编辑===

以下是正在运行的查询:

First Visit to site:

INSERT INTO onlineusers SET
ip = 123.456.789.123,
datetime = now(),
userid = 321,
page = '/thispage',
area = 'thisarea',
type = 3

On each page refresh:

UPDATE onlineusers SET
ips = 123.456.789.123,
datetime = now(),
userid = 321,
page = '/thispage',
area = 'thisarea',
type = 3
WHERE id = 888

Cron every 15 minutes:

DELETE FROM onlineusers WHERE datetime <= now() - INTERVAL 900 SECOND

然后,它会进行一些计数来记录一些统计数据(即:在线会员、在线访客).

推荐答案

一个可以帮助解决大多数死锁的简单技巧是按特定顺序对操作进行排序.

当两个事务试图以相反的顺序锁定两个锁时,就会出现死锁,即:

  • 连接1:锁 keys (1),锁 keys (2);
  • 连接2:锁 keys (2),锁 keys (1);

如果两个连接同时运行,连接1将锁定 keys (1),连接2将锁定 keys (2),每个连接将等待另一个释放 keys ->;僵局

现在,如果您更改查询,使连接以相同的顺序锁定密钥,即:

  • 连接1:锁 keys (1),锁 keys (2);
  • 连接2:锁 keys (1),锁 keys (2);

不可能出现僵局.

这就是我的建议:

  1. 确保除了delete语句外,没有其他查询一次锁定访问多个密钥.如果你这么做了(我猜你也这么做了),按升序排列它们在(k1,k2,…kn)中的位置.

  2. 修正delete语句,使其按升序运行:

改变

DELETE FROM onlineusers 
WHERE datetime <= now() - INTERVAL 900 SECOND

DELETE FROM onlineusers 
WHERE id IN (
    SELECT id FROM onlineusers
    WHERE datetime <= now() - INTERVAL 900 SECOND 
    ORDER BY id
) u;

另一件需要记住的事情是,MySQL文档建议,如果出现死锁,客户端应该自动重试.可以将此逻辑添加到客户端代码中.(比如,在放弃之前,对这个特定错误重试3次).

Mysql相关问答推荐

慢查询日志(log)甚至包括快速查询,因为它包括等待锁定所花费的时间

拒绝非超级用户访问停靠的MariaDB(超级用户工作)

SQL不断返回查询失败,并且不知道从这里到哪里go

MySQL grant语句中的用户名区分大小写

Hibernate 不创建表'

将时间戳四舍五入到最接近的半小时而不遗漏丢失的数据

基于多个 where 子句在规范化表中查找公共 ID(值)

MySQL 可以用于将列表排序为三分之三吗?

根据 Power Query 中的条件替换值

MySql SELECT 查找包含数字的区间的时间复杂度是多少?

过滤查询结果

Django中的MYSQL查询等效

在 SQL 中的 case 语句之后将新列转换为 INT

基于 2 列的重复行的 SQL 查询

sql sum - 处理连接表中的脏重复项

Laravel classloader.php 错误打开流失败:没有这样的文件或目录

ASP.NET EntityFramework 获取数据库名称

MySQL ORDER BY rand(),名称为 ASC

将 UTF8 表上的 latin1 字符转换为 UTF8

SELECT INTO 和未声明的变量错误