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

时间:2018-10-05 16:43:40

标签: javascript php mysql atomicity eventsource

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

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

我正在苦苦挣扎的是,在没有

的情况下,准确地为每个用户推送了哪些数据
  1. 不必要地更新不需要的列表实体
  2. 不丢失任何更新。

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

到目前为止的想法:

  1. 在我的JS中,我可以跟踪上一个请求的时间,并且在每个后续请求中,可以查询自该时间以来的数据。这不起作用,因为JS时间很可能与服务器MySQL时间不匹配。
  2. 在用户会话中存储服务器时间可能也可以做到。我觉得这可能在大多数时间都有效,但是根据数据库更新的时间和请求,更改可能会丢失,因为数据库仅以1秒的精度存储DateTime。

我敢肯定,还有一种更原子的方法可以做到这一点,尽管我只是在画一个空白。什么是适合的设计模式?

2 个答案:

答案 0 :(得分:0)

正确的是,您必须轮询数据库以查找更改,并且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查询之间的事务表行,则需要进行事务处理。

答案 1 :(得分:0)

O.Jones提供的解决方案可用于使跟踪更新原子化,尽管如果以下情况在一秒钟之内全部发生,则失败的地方是:

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

在这种情况下,下一个轮询操作将错过更新2,或者将重复更新1,具体取决于您在查询中使用>还是>=。这不是代码的错误,这是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";

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