【问题标题】:What's the fastest way to poll a MySQL table for new rows?轮询 MySQL 表以获取新行的最快方法是什么?
【发布时间】:2011-04-09 13:51:06
【问题描述】:

我的应用程序需要轮询 MySQL 数据库以获取新行。每次添加新行时,都应该检索它们。我正在考虑创建一个触发器来将对新行的引用放在单独的表上。原始表有超过 300,000 行。

应用程序是用 PHP 构建的。

一些很好的答案,我认为这个问题值得奖励。

【问题讨论】:

  • IMO,如果可能的话,无论您使用什么层插入,即包装 CRUD 操作的服务,都应该在插入后“通知”您的应用程序。这样你就不会经常轮询。
  • @Alex:它们是两个不同的独立应用程序。第二个应用程序只从数据库中读取。
  • 我想说 AFTER INSERT 触发器将在 MySQL 级别实现,并让脚本轮询和清理另一个表中的新条目。这样,即使强制另一个(非自动增量)id 仍然有效。

标签: php mysql triggers polling


【解决方案1】:

对于外部应用程序,我发现使用 TimeStamp 列是一种更强大的方法,它独立于自动 ID 和其他主键问题

向表中添加列,例如:

insertedOn TIMESTAMP DEFAULT CURRENT_TIMESTAMP

或跟踪插入和更新

updatedOn TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP

在外部应用程序中,您需要做的就是跟踪您进行投票时的最后一个时间戳。然后在所有相关表上从该时间戳中向前选择。在大型表中,您可能需要索引时间戳列

【讨论】:

  • 索引通常是有益的。当索引开销不值得时,有很多用例。通常一个表在每个基于 TIMESTAMP 的选择之间有许多插入和删除,并且基于 TIMESTAMP 的选择很少执行
  • 使用此解决方案需要注意的事项:如果执行轮询的应用程序正在批量获取更改(例如 SELECT * FROM TABLE WHERE updatedOn > :LAST_TIMESTAMP ORDER BY updateOn LIMIT 100),并且有可能一次更新超过批量大小(例如UPDATE TABLE SET COLUMN='VALUE' WHERE OTHER_COLUMN='SOMETHING THAT WILL SELECT HUNDREDS OF ROWS')然后你会错过行。
  • 这可能很容易不可靠,具体取决于插入量 - 多个插入可以接收相同的时间戳。这当然可以通过一些聪明的方法来解决。我选择了一个自动递增的主键,但是上面描述的基于触发器的版本你可以更新一个基于内存的表,它可以被非常快速地轮询。
  • @AdamByrtek 这并不总是有益的,您可能不需要对特定的时间戳进行查询或排序,索引不是免费的
【解决方案2】:

您可以使用以下语句来查看表中是否插入了新记录:

select max(id) from table_name

替换上面语句中的主键名和表名。将 max(id) 值保存在临时变量中,并检索此值与上次保存的 max(id) 值之间的所有新记录。获取新记录后,将 max(id) 值设置为您从查询中获得的值。

【讨论】:

  • 为什么不选择 * from table_name where id > :max
【解决方案3】:

创建一个 PHP 守护程序来监控 MySQL 表文件的大小,如果大小发生变化,则查询新记录,如果找到新记录,则运行下一个进程。

我认为您可以轻松配置一个活动的 PEAR 守护程序来监控 MySQL 表文件大小并启动​​您的脚本。

【讨论】:

  • 我不确定 MySQL,但通常表空间是按块分配的,因此一旦分配完成,可以在需要再次分配之前添加几行。
  • 如果使用innodb,许多表在同一个文件中。
【解决方案4】:

假设您有一个身份或其他一些一直在增长的数据,您应该跟踪检索到的最后一个 id 的 php 应用程序。

这适用于大多数情况。除非您参加实时训练营,否则我认为您不需要更多。

【讨论】:

    【解决方案5】:

    我会做这样的事情。当然,这是假设 ID 是递增的数字 ID。 以及如何在数据库中存储“当前位置”取决于您。

    <?
    $idFile = 'lastID.dat';
    
    if(is_file($idFile)){
        $lastSelectedId = (int)file_get_contents($idFile);
    } else {
        $lastSelectedId = 0;
    }
    
    $res = mysql_query("select * from table_name where id > {$lastSelectedId}");
    
    while($row = mysql_fetch_assoc($res)){
        // Do something with the new rows
    
        if($row['id']>$lastSelectedId){
            $lastSelectedId = $row['id'];
        }
    }
    
    file_put_contents($idFile,$lastSelectedId);
    
    ?>
    

    【讨论】:

      【解决方案6】:

      我同意 TFD 的回答,即在单独的文件/表中跟踪时间戳,然后获取所有更新的行。对于类似的应用程序,我就是这样做的。

      您的应用程序查询单行表(或文件)以查看时间戳是否已从本地存储更改,这不会对性能造成太大影响。然后,假设时间戳被正确索引,根据时间戳从 300k 行表中获取新行应该再次没问题。

      但是,在阅读您的问题时,我很好奇 Mysql 触发器是否可以进行系统调用,比如说一个 php 脚本可以做一些繁重的工作。原来they can 使用sys_exec() User-Defined Function。您可以使用它通过将插入的行数据传递给它来进行各种处理,本质上是即时通知插入。

      最后,a word of caution 关于使用触发器调用外部应用程序。

      【讨论】:

        【解决方案7】:

        一种选择可能是使用 INSERT INTO SELECT 语句。从使用时间戳提取最新行的建议中获取,您可以执行类似...

        INSERT INTO t2 (
            SELECT * 
            FROM t1 
            WHERE createdts > DATE_SUB(NOW(), INTERVAL 1 HOUR)
        );
        

        这将获取前一小时插入的所有行并将它们插入到表 2 中。您可以让脚本运行此查询并让它每小时运行一次(或任何您需要的时间间隔)。

        这将大大简化您提取行的 PHP 脚本,因为您不需要遍历任何行。它还摆脱了跟踪最后插入 id 的麻烦。

        Fanis 的解决方案听起来也很有趣。

        请注意,上述插入中的选择查询可以调整为仅插入某些字段。如果您只需要某些字段,则需要像这样在插入中指定它们...

        INSERT INTO t2 (field1, field2) (
            SELECT field1, field2 
            FROM t1 
            WHERE createdts > DATE_SUB(NOW(), INTERVAL 1 HOUR)
        );
        

        【讨论】:

          猜你喜欢
          • 2018-07-08
          • 2021-06-21
          • 1970-01-01
          • 1970-01-01
          • 2016-02-14
          • 1970-01-01
          • 1970-01-01
          • 2017-10-25
          • 1970-01-01
          相关资源
          最近更新 更多