【问题标题】:Methods for tracking changes when making realtime updates to a webpage对网页进行实时更新时跟踪更改的方法
【发布时间】:2018-10-05 16:43:40
【问题描述】:

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

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

我正在苦苦挣扎的是弄清楚在没有

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

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

目前的想法:

  1. 在我的 JS 中,我可以跟踪最后一次请求的时间,并在每个后续请求中,询问从那时起的数据。这不起作用,因为 JS 时间很可能与服务器 MySQL 时间不匹配。
  2. 同样可以在用户会话中存储服务器时间。我觉得这可能在大多数情况下都有效,但根据数据库更新的时间和请求,可能会错过更改,因为数据库只存储精度为 1 秒的 DateTime。

我确信有一种更原子的方法可以做到这一点,但我只是画了一个空白。什么是合适的设计模式?

【问题讨论】:

    标签: javascript php mysql atomic eventsource


    【解决方案1】:

    您是正确的,您必须轮询数据库以进行更改,并且 MySQL 不能将更改推送到其他应用程序。

    诀窍是在整个轮询中使用服务器时间。使用表格来跟踪轮询。例如,假设您的用户具有 user_id 值。然后制作一个poll 表,其中包含

     user_id  INT primary key
     polldate DATETIME 
    

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

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

     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 表的 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 表中获取自上次投票以来已更新的项目。如果没有项目,polldate 不会更改。而且,这一切都在服务器时间。

    如果其他客户端更新您的 SELECT 和 UPDATE 查询之间的事务表行,您需要该事务。

    【讨论】:

      【解决方案2】:

      O.Jones 提供的解决方案可以使跟踪更新原子化,但如果以下情况在一秒钟内发生,它就会失败:

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

      在这种情况下,下一个轮询操作将错过更新 2,或重复更新 1,具体取决于您在查询中使用的是 &gt; 还是 &gt;=。这不是代码的错,这是 MySql datetime 类型的限制,只有 1 秒的分辨率。这可以通过 MySql v8 得到一定程度的缓解,因为它有 Fractional Seconds Support,尽管这仍然不能保证原子性。

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

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

      每当对订单进行更改时,都会更新此表,本质上是对每次更新进行计数。

      对于客户端,服务器存储在会话中发送的来自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";
      

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

      【讨论】:

        猜你喜欢
        • 2013-10-06
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2019-03-08
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多