对网页进行实时更新时跟踪更改的方法

Methods for tracking changes when making realtime updates to a webpage

我希望在网页上实时更新订单(和状态)列表。 (MySQL) 数据库中的订单通过其他进程 (PHP) 异步更新。

我熟悉将数据推送到页面的机制(轮询、事件源)。这不是关于那个的。

我正在努力解决的问题是在没有

的情况下准确地为每个用户推送哪些数据
  1. 不必要地更新不需要的列表实体
  2. 没有错过更新。

我的 table 确实有一个 DateTime 列 last_update_date,我会在订单有任何更改时更新它。我知道 MySQL 实际上没有任何可以触发其他代码的事件触发器。

目前的想法:

  1. 在我的 JS 中,我可以跟踪上次请求的时间,并且在每个后续请求中,从那个时间开始请求数据。这不起作用,因为 JS 时间很可能与服务器 MySQL 时间不匹配。
  2. 同样可以将服务器时间存储在用户会话中。我觉得这可能在大部分时间都有效,但是根据数据库更新和请求的时间,更改可能会被遗漏,因为数据库只存储精度为 1 秒的 DateTime。

我确定有更原子的方法可以做到这一点,不过我只是在画一个空白。什么是 suitable 设计模式?

您必须轮询数据库以获取更改,并且 MySQL 无法将更改推送到其他应用程序,这是正确的。

诀窍是在整个轮询中使用服务器时间。使用 table 来跟踪轮询。例如,假设您的用户有 user_id 个值。然后做一个polltable组成的

 user_id  INT primary key
 polldate DATETIME 

然后,当您轮询时执行此序列。

首先确保您的用户在 poll table 中有一个显示很久以前 polldate 的条目。 (INSERT IGNORE 不会覆盖 table 中的任何现有行。)

 SET @userid := <<your user's id>>;
 INSERT IGNORE INTO poll (user_id, polldate) VALUES (@userid, '1970-01-01')

然后当你轮询的时候,做这一系列的操作。

锁定用户的投票行:

 BEGIN TRANSACTION;
 SELECT polldate INTO @polldate
   FROM poll
  WHERE user_id = @userid 
    FOR UPDATE;

检索您需要的更新行;自上次更新以来的那些。

 SELECT t.whatever, t.whatelse
   FROM transaction_table t
   JOIN poll p ON t.user_id = p.user_id
  WHERE user_id = @userid
    AND t.last_update_date > p.polldate;

更新 poll table 的 polldate 列

UPDATE poll p
   SET p.polldate = IFNULL(MAX(t.last_update_date), p.polldate)
  FROM transaction_table t
  JOIN poll_p ON t.user_id = p.user_id
  WHERE user_id = @userid
    AND t.last_update_date > p.polldate;

并提交事务。

 COMMIT;

每次使用此序列时,您都会从 transaction table 中获取自上次投票以来已更新的项目。如果没有项目,polldate 不会改变。而且,这一切都在服务器时间。

您需要该事务以防其他客户端更新您的 SELECT 和您的 UPDATE 查询之间的事务 table 行。

所提供的解决方案 O.Jones 可以使跟踪更新成为原子操作,但它失败的地方是如果以下情况在一秒钟内全部发生:

  1. 订单更新已写入 table(更新 1)
  2. 发生轮询操作
  3. 订单更新已写入 table(更新 2)

在这种情况下,下一个轮询操作将错过更新 2,或者将重复更新 1,具体取决于您在查询中使用 > 还是 >=。这不是代码的错误,这是 MySql 日期时间类型只有 1 秒分辨率的限制。 MySql v8 可以稍微缓解这种情况,因为它具有 Fractional Seconds Support,尽管这仍然不能保证原子性。

我最终使用的解决方案是创建一个 order_changelog table

CREATE TABLE 'NewTable' (
'id'  int NULL AUTO_INCREMENT ,
'order_id'  int NULL ,
'update_date'  datetime NULL ,
PRIMARY KEY ('id')
);

每当对订单进行更改时,此 table 都会更新,本质上是对每次更新进行编号。

对于客户端,服务器存储 order_changelog 在会话中发送的最后一个 ID。每次客户端轮询时,我都会从 order_changelog 中获取 ID 大于会话中存储的 ID 的所有行,并将订单加入其中。

$last_id = $_SESSION['last_update_id'];

$sql = "SELECT o.*, c.id as update_id
                FROM order_changelog c
                LEFT JOIN orders o ON c.order_id = o.id
                WHERE c.id > $last_id
                GROUP BY o.id
                ORDER BY order_date";

我现在可以保证拥有自上次投票以来的所有订单,没有重复,而且我不必跟踪单个客户。