代码之家  ›  专栏  ›  技术社区  ›  Tom

MySQL—如何有效地获取ID最低的行?

  •  5
  • Tom  · 技术社区  · 14 年前

    有没有比使用 ORDER BY id LIMIT 1 在下面的查询中?

    UPDATE mytable SET field1 = '1' WHERE field1 = 0 ORDER BY id LIMIT 1;
    

    注:

    • 假设主键是 id 还有一个索引 field1 .
    • 我们正在更新 单行 .
    • 我们不是严格更新最旧的行,而是更新最旧的行 .
    • 我们要更新 最旧匹配行 身份证件 ,即FIFO队列的头。

    问题:

    • ORDER BY id 有必要吗?默认情况下MySQL是如何排序的?

    现实世界的例子

    我们有一个DB表用于电子邮件队列。当我们要对发送给用户的电子邮件进行排队时,会添加行。行被cron作业删除,每分钟运行一次,在那一分钟内处理尽可能多的行,每行发送一封电子邮件。

    我们打算放弃这种方法,使用类似 Gearman Resque 处理我们的电子邮件队列。但与此同时,我有一个问题,即如何有效地标记队列中最旧的项以进行处理,也就是ID最低的行。此查询的作用是:

    mysql_query("UPDATE email_queue SET processingID = '1' WHERE processingID = 0 ORDER BY id LIMIT 1");
    

    但是,由于缩放问题,它经常出现在mysql的慢日志中。当表有500000行时,查询可能需要10秒以上的时间。问题是,这个表自从第一次被引入以来增长了很多,现在有时有50万行,开销为133.9mib。例如,我们每天插入6000个新行,大概180次,然后删除大致相同的数量。

    为了停止查询出现在慢日志中,我们删除了 按id排序 来阻止整个桌子。即

    mysql_query("UPDATE email_queue SET processingID = '1' WHERE processingID = 0 LIMIT 1");
    

    ... 但是新的查询不再总是获得具有最低id的行(尽管它经常这样做)。除了使用 按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;
    
    5 回复  |  直到 14 年前
        1
  •  3
  •   shamittomar    14 年前
        2
  •  1
  •   Jon Black    14 年前

    你有没有考虑过用innodb的方式及时更新innodb?

        3
  •  1
  •   Hendra Jaya    14 年前

    我认为“慢的部分”来自

    WHERE processingID = 0 
    

    它很慢,因为它没有索引。但是,索引这个专栏(IMHO)似乎也不正确。

    WHERE id = 0 
    

    从理论上讲,它会更快,因为它使用了索引。

    创建另一个包含 id 未处理的行数?因此,插入操作需要两次。第二个是插入,第二个是插入 身份证件 到“尚未处理的表”中。处理部分也需要加倍的责任。首先检索 身份证件 从“尚未处理的表”中删除它。加工部分的第二项工作当然是加工。

    当然,这个 身份证件 “尚未处理的表”中的列需要索引其内容。只是为了确保选择和删除的速度更快。

        4
  •  1
  •   Timo    6 年前

    你有处理ID的条件( WHERE processingID = 0 ),和 在这种限制下 您要按ID订购。

    我们如何改进这一点?

    假设你有一个索引 processingID . 从技术上讲,主键总是被附加的(这就是索引如何“指向”任何东西)。所以你呢 真正地 在上有索引 processingID, id . 这意味着订购的速度会很快。

    将您的订单更改为: ORDER BY processingID, id

    因为您已经将processingID固定为一个值 WHERE 子句,这不会更改生成的顺序。然而,它 容易的 使数据库同时应用您的条件和您的订单,而不扫描 任何 不匹配的记录。

        5
  •  0
  •   rtacconi    14 年前

    一件有趣的事情是,默认情况下,MySQL返回按ID排序的行,而不是像关系理论中所说的那样以一种随意的方式返回(我不确定这种行为是否在最新版本中发生了更改)。因此,从select中得到的最后一行应该是最后插入的行。当然,我不会用这种方式。

    正如您所说,最好的解决方案是使用Resque或RabbitMQ&co。

    您可以使用内存中的表,这是易变的,但比存储最新的ID快得多,或者使用my峈isam表来添加持久性。它在性能上简单而快速,并且需要一点时间来实现。