最近我们讨论了如何处理竞争条件,我想让两个知道我对innodb中的锁定和事务模型的理解在以下情况下是否正确(或者我可能遗漏了什么):
给定以下数据库表:
CREATE TABLE `requests` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`name` varchar(32) NOT NULL,
`status` enum('queuing','processing') NOT NULL DEFAULT 'queuing',
`pid` varchar(255) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
此表中有 3 条记录 R1、R2 和 R3(所有三条记录的状态队列和 pid = null)和两个进程 P1 和 P2。
P1 运行以下查询,将下一个排队条目分配给自身。
UPDATE requests SET status = 'processing', pid = '1'
WHERE status = 'queuing'
AND pid IS NULL
ORDER BY id
LIMIT 1;
这将在 r1 中为 T1 设置独占记录锁。
现在 P2 运行更新查询
UPDATE requests
SET status = 'processing', pid = '2'
WHERE status = 'queuing'
AND pid IS NULL
ORDER BY id
LIMIT 1;
并请求 R1 的独占记录锁。由于此锁当前由 T1 持有,因此不会向 T2 授予锁定 -> T2 会等到 T1 提交
P1 运行
SELECT * FROM requests WHERE status = 'processing' AND pid = '1';
并获取 R1。 P1 提交 T1,对 r1 所做的更改对每个人都可见。
T2 继续,由于 r1 现在具有状态处理和 pid != null,因此对 r2 运行更新。 P2 选择 r2 并提交事务 T2 - 对 r2 所做的更改对每个人都可见。
谢谢你的帮助。
我不会按照问题回答问题,而是要解决根本问题:"这把锁给我带来了麻烦;我能做些什么呢?
INDEX(status, pid, id)
可能正在发生的事情是,您必须在表中扫描相当远的位置,同时阻止其他人。 使用此索引,它应该能够更快地向下钻取 BTree 到下一项。 "更快"意味着死锁的可能性更小。
缺点是新索引需要混乱的更新。
我的口头禅是"不要排队,去做就行了。 我的意思是,在MySQL中构建排队机制的开销可能比收益更多。
此外,在自己的事务中运行UPDATE
,而不是一些更大的代码块的一部分。