我希望实时更新网页上的订单(和状态)列表。 (MySQL)数据库中的订单通过其他进程(PHP)异步更新。
我熟悉将数据推送到页面的机制(轮询,事件源)。不是这个。
我正在苦苦挣扎的是,在没有
的情况下,准确地为每个用户推送了哪些数据我的表中确实有一个DateTime列last_update_date
,当订单有任何更改时,我会对其进行更新。我知道MySQL确实没有任何可以触发其他代码的事件触发器。
到目前为止的想法:
我敢肯定,还有一种更原子的方法可以做到这一点,尽管我只是在画一个空白。什么是适合的设计模式?
答案 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提供的解决方案可用于使跟踪更新原子化,尽管如果以下情况在一秒钟之内全部发生,则失败的地方是:
在这种情况下,下一个轮询操作将错过更新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";
我现在可以保证自上次民意调查以来拥有所有订单,没有重复,而且我不必跟踪单个客户。