对网页进行实时更新时跟踪更改的方法
Methods for tracking changes when making realtime updates to a webpage
我希望在网页上实时更新订单(和状态)列表。 (MySQL) 数据库中的订单通过其他进程 (PHP) 异步更新。
我熟悉将数据推送到页面的机制(轮询、事件源)。这不是关于那个的。
我正在努力解决的问题是在没有
的情况下准确地为每个用户推送哪些数据
- 不必要地更新不需要的列表实体
- 没有错过更新。
我的 table 确实有一个 DateTime 列 last_update_date
,我会在订单有任何更改时更新它。我知道 MySQL 实际上没有任何可以触发其他代码的事件触发器。
目前的想法:
- 在我的 JS 中,我可以跟踪上次请求的时间,并且在每个后续请求中,从那个时间开始请求数据。这不起作用,因为 JS 时间很可能与服务器 MySQL 时间不匹配。
- 同样可以将服务器时间存储在用户会话中。我觉得这可能在大部分时间都有效,但是根据数据库更新和请求的时间,更改可能会被遗漏,因为数据库只存储精度为 1 秒的 DateTime。
我确定有更原子的方法可以做到这一点,不过我只是在画一个空白。什么是 suitable 设计模式?
您必须轮询数据库以获取更改,并且 MySQL 无法将更改推送到其他应用程序,这是正确的。
诀窍是在整个轮询中使用服务器时间。使用 table 来跟踪轮询。例如,假设您的用户有 user_id 个值。然后做一个poll
table组成的
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 可以使跟踪更新成为原子操作,但它失败的地方是如果以下情况在一秒钟内全部发生:
- 订单更新已写入 table(更新 1)
- 发生轮询操作
- 订单更新已写入 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";
我现在可以保证拥有自上次投票以来的所有订单,没有重复,而且我不必跟踪单个客户。
我希望在网页上实时更新订单(和状态)列表。 (MySQL) 数据库中的订单通过其他进程 (PHP) 异步更新。
我熟悉将数据推送到页面的机制(轮询、事件源)。这不是关于那个的。
我正在努力解决的问题是在没有
的情况下准确地为每个用户推送哪些数据- 不必要地更新不需要的列表实体
- 没有错过更新。
我的 table 确实有一个 DateTime 列 last_update_date
,我会在订单有任何更改时更新它。我知道 MySQL 实际上没有任何可以触发其他代码的事件触发器。
目前的想法:
- 在我的 JS 中,我可以跟踪上次请求的时间,并且在每个后续请求中,从那个时间开始请求数据。这不起作用,因为 JS 时间很可能与服务器 MySQL 时间不匹配。
- 同样可以将服务器时间存储在用户会话中。我觉得这可能在大部分时间都有效,但是根据数据库更新和请求的时间,更改可能会被遗漏,因为数据库只存储精度为 1 秒的 DateTime。
我确定有更原子的方法可以做到这一点,不过我只是在画一个空白。什么是 suitable 设计模式?
您必须轮询数据库以获取更改,并且 MySQL 无法将更改推送到其他应用程序,这是正确的。
诀窍是在整个轮询中使用服务器时间。使用 table 来跟踪轮询。例如,假设您的用户有 user_id 个值。然后做一个poll
table组成的
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 可以使跟踪更新成为原子操作,但它失败的地方是如果以下情况在一秒钟内全部发生:
- 订单更新已写入 table(更新 1)
- 发生轮询操作
- 订单更新已写入 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";
我现在可以保证拥有自上次投票以来的所有订单,没有重复,而且我不必跟踪单个客户。