ORDER BY id LIMIT 1
有没有比在以下查询中使用更快速的方法来更新与特定条件匹配的 MySQL 表的最旧行?
UPDATE mytable SET field1 = '1' WHERE field1 = 0 ORDER BY id LIMIT 1;
笔记:
- 假设主键是
id
并且在 上也有一个索引field1
。 - 我们正在更新一行。
- 我们不是严格更新最旧的行,而是更新与条件匹配的最旧的行。
- 我们要更新最旧的匹配行,即最低的
id
,即 FIFO 队列的头部。
问题:
- 有
ORDER BY id
必要吗?MySQL默认如何排序?
现实世界的例子
我们有一个用于电子邮件队列的数据库表。当我们想要将电子邮件排队发送给我们的用户时,会添加行。行由 cron 作业删除,每分钟运行一次,在那一分钟内处理尽可能多的行,每行发送 1 封电子邮件。
我们计划放弃这种方法,使用Gearman或Resque 之类的工具来处理我们的电子邮件队列。但与此同时,我有一个问题,我们如何有效地标记队列中最旧的项目以进行处理,也就是 ID 最低的行。这个查询完成了这项工作:
mysql_query("UPDATE email_queue SET processingID = '1' WHERE processingID = 0 ORDER BY id LIMIT 1");
但是,由于缩放问题,它经常出现在 mysql 慢日志中。当表有 500,000 行时,查询可能需要 10 秒以上。问题是该表自首次引入以来已经大幅增长,现在有时有 50 万行和 133.9 MiB 的开销。例如,我们每天可能插入 6000 条新行 180 次并删除大致相同的数量。
为了停止出现在慢日志中的查询,我们删除了ORDER BY id
以停止整个表的大量排序。IE
mysql_query("UPDATE email_queue SET processingID = '1' WHERE processingID = 0 LIMIT 1");
...但是新查询不再总是获取具有最低 id 的行(尽管它经常这样做)。除了 using 之外,是否有更有效的方法来获取具有最低 id 的行ORDER BY id
?
作为参考,这是电子邮件队列表的结构:
CREATE TABLE IF NOT EXISTS `email_queue` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`time_queued` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT 'Time when item was queued',
`mem_id` int(10) NOT NULL,
`email` varchar(150) NOT NULL,
`processingID` int(2) NOT NULL COMMENT 'Indicate if row is being processed',
PRIMARY KEY (`id`),
KEY `processingID` (`processingID`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1;