例如,假设我有一个Cloud
环境和一个Client
环境,我想将大量数据从云同步到客户端。假设我在云中有一个名为Files
的数据库表,并且我希望客户端环境中存在完全相同的表。
现在让我们假设几件事:
- 文件表非常大。
- 文件中每一行的数据可以随时更新,并具有
last-update
列。 - 我想获取增量并确保我在两个环境中都是相同的。
我的解决方案:
- 我首先进行完全同步,将所有条目返回给客户端。
- 我在客户端环境中保留
LastSync
时间,并从LastSync
时间开始同步增量。 - 我使用分页进行完全同步和增量同步:客户端将触发第一个请求,以获取增量结果的
Count
以及每个请求的Page Size
所需的许多其他请求。
例如,计数:
SELECT COUNT(*) FROM files WHERE last_update > @LastSyncTime
页面抓取:
SELECT col1, col2..
FROM files
WHERE last_update > @LastSyncTime
ORDER BY files.id
LIMIT @LIMIT
OFFSET @OFFSET
我的问题:
例如,如果第一次抓取(Count
抓取(需要一些时间(例如几分钟(,并且在此期间更新了更多条目并将其添加到last-update
抓取中,该怎么办?
例如:
- 计数获取给出了 100 个条目
last-update 1000 seconds
. - 获取
Count
时更新了 1 个条目。 - 现在
last-update 1000 seconds
将提供 101 个条目。 - 页面提取只会从 101 个条目中获取 100 个条目 ,并按
- 缺少 1 个条目且未同步到客户端
id
排序我尝试了其他 2 种选择:
- 与
from-to
日期限制同步last-update
. - 按
last-update
而不是id
列排序。
我在这两个选项中都看到了问题。
-
不要使用
OFFSET
和LIMIT
;它从正常到慢到慢。 相反,使用last_update
跟踪"您离开的地方",以便提高效率。更多讨论 -
由于日期时间可能存在重复值,因此请灵活选择一次执行多少行。
-
持续运行。 不要使用 cron,除非作为"保持活动"。
-
不需要初始副本;此代码会为您完成。
-
拥有
INDEX(last_update)
至关重要
这是代码:
-- Initialize. Note: This subtract is consistent with the later compare.
SELECT @left_off := MIN(last_update) - INTERVAL 1 DAY
FROM tbl;
Loop:
-- Get the ending timestamp:
SELECT @cutoff := last_update FROM tbl
WHERE last_update > @left_off
ORDER BY last_update
LIMIT 1 OFFSET 100; -- assuming you decide to do 100 at a time
-- if no result, sleep for a while, then restart
-- Get all the rows through that timestamp
-- This might be more than 100 rows
SELECT * FROM tbl
WHERE last_update > @left_off
AND last_update <= @cutoff
ORDER BY last_update
-- and transfer them
-- prep for next iteration
SET @left_off := @cutoff;
Goto Loop
SELECT @cutoff
会很快 - 它是对索引中 100 个连续行的简短扫描。
SELECT *
承担繁重的工作,并且花费的时间与行数成正比 - 没有额外的OFFSET
开销。 读取 100 行大约需要 1 秒(假设旋转磁盘,非缓存数据(。
与其最初获得COUNT(*)
,我会从获取MAX(last_update)
开始,因为其余代码都基于last_update
。 此查询是"即时的",因为它只需要探测索引的末尾。 但我声称你甚至不需要那个!
一个可能的错误:如果可以删除"源"中的行,您如何识别?
根据数据的大小以及它是"公共"使用还是可以在多个客户端之间使用,拆分内容可能会有所帮助。 例如,创建每日"增量"的完整数据集并缓存它们。这样,数据库就不需要在第一次加载时一遍又一遍地查询每个客户端需要的数据。
- 尝试尽可能减少对大数据表的访问(如果数据完全没有变化,则异地缓存(。
- 卸载和缓存经常使用的查询数据。 因此,您可以减少 SQL 查询的数量。
- 在
last_update
上创建一个索引,id
应该有助于加快速度,以便从数据库中实时获取增量线。
可能的解决方案:
-
每当有一些新项目时,每小时/完整集每隔x次创建一次数据库。
-
客户端在第一次从缓存中提取时获取"每日增量/每小时增量"。
-
客户端直接从数据库中获取自上次增量"最新项目"以来的所有项目。
可能会有所帮助:
- 使用查询通知更新缓存:SQL Server 中的 MSSQL 查询通知
- 使用
MySQL
时:在MySQL中通知事件侦听器
您的方法涵盖了许多解决方法,您遵循了错误的方式。
开始考虑数据库复制,它将抽象所有这些解决方法,并为您提供解决其此类问题的工具。
一篇关于最近MySQL服务器组复制的优秀文章: https://www.digitalocean.com/community/tutorials/how-to-configure-mysql-group-replication-on-ubuntu-16-04