使用分页获取大量数据
Fetching large loads of data using paging
例如,假设我有一个 Cloud
环境和一个 Client
环境,我想将大量数据从云端同步到客户端。假设我在名为 Files
的云中有一个数据库 table,我希望在客户端环境中存在完全相同的 table。
现在让我们假设一些事情:
- 文件table很大。
- 文件中每一行的数据可以随时更新,并且有
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
提取中怎么办.
例如:
- 计数提取为
last-update 1000 seconds
提供了 100 个条目。
- 1 个条目在获取
Count
时更新。
- 现在
last-update 1000 seconds
将给出 101 个条目。
- 页面提取只会从 101 中获取 100 个条目,顺序为
id
- 1 个条目丢失且未同步到客户端
我尝试了另外两种选择:
- 同步
from-to
日期限制 last-update
。
- 按
last-update
而不是 id
列排序。
我发现这两个选项都有问题。
根据数据的大小以及是否使用 'public' 或可以在多个客户端之间使用,拆分数据可能会有所帮助。
例如,创建每日“增量”完整数据集并缓存它们。这样就不需要在第一次加载时一遍又一遍地查询数据库以获取每个客户端需要的数据。
- 尽量减少对大数据的访问table(如果数据根本没有变化,就异地缓存)。
- 卸载并缓存经常使用的查询频繁的数据。所以你减少了 SQL 查询的数量。
- 在
last_update
上创建索引,id
应该有助于加快速度,以便从数据库中实时获取增量行。
可能的解决方案:
数据库每小时创建一次/每当有新项目出现时,每 x 次完成一次。
客户端在第一次从缓存中获取 "Daily Delta/Hourly delta"。
客户端直接从数据库中获取自上次增量 "latest items" 以来的所有项目。
可能会有帮助:
- 使用查询通知更新缓存:MSSQL Query Notifications in SQL Server
- 使用
MySQL
时:Notify Event Listener in MySQL
不要使用OFFSET
和LIMIT
;它从好到慢再慢。相反,请使用 last_update
跟踪 "where you left off",这样效率会更高。 More Discussion
由于日期时间可能重复,因此请灵活选择一次处理多少行。
运行这样不断。不要将 cron 用作 'keep-alive'.
不需要初始副本;此代码为您完成。
拥有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
。这个查询是 "instantaneous" 因为它只需要探测索引的末尾。但我声称你甚至不需要那个!
可能的错误:如果 'source' 中的行可以删除,您如何识别?
您的方法涵盖了很多解决方法,您走错了路。
开始考虑数据库复制,它将提取所有这些解决方法,并为您提供解决此类问题的工具。
一篇关于最近 MySQL 服务器的组复制的优秀文章:
https://www.digitalocean.com/community/tutorials/how-to-configure-mysql-group-replication-on-ubuntu-16-04
例如,假设我有一个 Cloud
环境和一个 Client
环境,我想将大量数据从云端同步到客户端。假设我在名为 Files
的云中有一个数据库 table,我希望在客户端环境中存在完全相同的 table。
现在让我们假设一些事情:
- 文件table很大。
- 文件中每一行的数据可以随时更新,并且有
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
提取中怎么办.
例如:
- 计数提取为
last-update 1000 seconds
提供了 100 个条目。 - 1 个条目在获取
Count
时更新。 - 现在
last-update 1000 seconds
将给出 101 个条目。 - 页面提取只会从 101 中获取 100 个条目,顺序为
id
- 1 个条目丢失且未同步到客户端
我尝试了另外两种选择:
- 同步
from-to
日期限制last-update
。 - 按
last-update
而不是id
列排序。
我发现这两个选项都有问题。
根据数据的大小以及是否使用 'public' 或可以在多个客户端之间使用,拆分数据可能会有所帮助。 例如,创建每日“增量”完整数据集并缓存它们。这样就不需要在第一次加载时一遍又一遍地查询数据库以获取每个客户端需要的数据。
- 尽量减少对大数据的访问table(如果数据根本没有变化,就异地缓存)。
- 卸载并缓存经常使用的查询频繁的数据。所以你减少了 SQL 查询的数量。
- 在
last_update
上创建索引,id
应该有助于加快速度,以便从数据库中实时获取增量行。
可能的解决方案:
数据库每小时创建一次/每当有新项目出现时,每 x 次完成一次。
客户端在第一次从缓存中获取 "Daily Delta/Hourly delta"。
客户端直接从数据库中获取自上次增量 "latest items" 以来的所有项目。
可能会有帮助:
- 使用查询通知更新缓存:MSSQL Query Notifications in SQL Server
- 使用
MySQL
时:Notify Event Listener in MySQL
不要使用
OFFSET
和LIMIT
;它从好到慢再慢。相反,请使用last_update
跟踪 "where you left off",这样效率会更高。 More Discussion由于日期时间可能重复,因此请灵活选择一次处理多少行。
运行这样不断。不要将 cron 用作 'keep-alive'.
不需要初始副本;此代码为您完成。
拥有
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
。这个查询是 "instantaneous" 因为它只需要探测索引的末尾。但我声称你甚至不需要那个!
可能的错误:如果 'source' 中的行可以删除,您如何识别?
您的方法涵盖了很多解决方法,您走错了路。
开始考虑数据库复制,它将提取所有这些解决方法,并为您提供解决此类问题的工具。
一篇关于最近 MySQL 服务器的组复制的优秀文章: https://www.digitalocean.com/community/tutorials/how-to-configure-mysql-group-replication-on-ubuntu-16-04