【问题标题】:Fetching large loads of data using paging使用分页获取大量数据
【发布时间】:2023-03-17 10:05:01
【问题描述】:

假设我有一个Cloud 环境和一个Client 环境,我想将大量数据从云同步到客户端。假设我在云中有一个名为Files 的数据库表,我希望客户端环境中存在完全相同的表。

现在让我们假设一些事情:

  1. 文件表很大。
  2. 文件中每一行的数据可以随时更新,并有一个last-update列。
  3. 我想获取增量并确保我在两个环境中都是相同的。

我的解决方案:

  1. 我先进行完全同步,将所有条目返回给客户端。
  2. 我将LastSync 时间保留在客户端环境中,并从LastSync 时间开始同步增量。
  3. 我使用分页进行完全同步和增量同步:客户端将触发第一个请求以获取增量结果的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 提取中。

例如:

  • Count fetch 为 last-update 1000 seconds 提供了 100 个条目。
  • 在获取 Count 时更新了 1 个条目。
  • 现在last-update 1000 seconds 将提供 101 个条目。
  • 页面获取只会从 101 中获取 100 个条目,顺序为 id
  • 1 个条目丢失且未同步到客户端

我尝试了其他 2 个选项:

  • from-to 日期限制同步last-update
  • last-update 而非id 列排序。

我发现这两个选项都有问题。

【问题讨论】:

    标签: c# mysql paging data-paging


    【解决方案1】:
    • 不要使用OFFSETLIMIT;它从好到慢到慢。相反,请使用last_update 跟踪“您离开的位置”,以便提高效率。 More Discussion

    • 由于日期时间可能存在重复,因此请灵活选择一次执行多少行。

    • 持续运行。不要将 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 秒(假设旋转磁盘、非缓存数据)。

    我将首先获取MAX(last_update),而不是最初获取COUNT(*),因为其余代码基于last_update。这个查询是“即时的”,因为它只需要探测索引的末尾。但我声称你甚至不需要那个!

    一个可能的错误:如果可以删除“源”中的行,您如何识别?

    【讨论】:

    • 我按照我写的那样通过last_update 进行所有查询(当然是索引),但想想第一个请求,在我的情况下,第一个请求中可能有数百万行..(如客户端如果通过 min_date(last_update)) 获取数据,所以他需要同步整个表,这就是为什么我必须使用分页..
    • 所以问题是关于第一次同步(或重增量同步),你必须用分页保护它,否则我会超时
    • @omriman12 - 您有数百万行与完全相同 last_update?如果是这样,我们需要考虑切换到id。如果不是,我的“页面”限制为 100 +(相同 last_update 值的最大数量);从首页页开始。
    • 我明白你的意思,所以如果我将它带回我的客户端-服务器示例,那么我的客户端将像我一样保留last_update(即left_off),并且只需从服务器获取 1 个批次(例如 100 行),服务器将返回新的left_off 日期,依此类推.. 是的,这种方法可以解决我的问题
    【解决方案2】:

    根据数据的大小,如果它是“公共的”或者可以在多个客户端之间使用,那么拆分数据可能会有所帮助。 例如,创建每日“增量”完整数据集并缓存它们。这样一来,数据库就不需要在第一次加载时一遍又一遍地查询每个客户端需要的数据。

    1. 尽量减少对大数据表的访问(如果数据完全没有变化,缓存异地)。
    2. 卸载和缓存经常使用的查询很多的数据。从而减少 SQL 查询的数量。
    3. last_updateid 上创建索引应该有助于加快从数据库中实时获取增量线的速度。

    可能的解决方案:

    1. 每当有一些新项目出现时,数据库每 x 次每小时创建一次/完成集。

    2. 客户端在第一次从缓存中获取时获得“每日增量/每小时增量”。

    3. 客户端直接从数据库中获取自上次增量“最新项目”以来的所有项目。

    可能会有所帮助:

    【讨论】:

    • 但是我从来没有谈过性能,我说的是一个关于分页的逻辑问题
    【解决方案3】:

    你的方法涵盖了很多变通方法,你走错路了。

    开始考虑数据库复制,它将抽象出所有这些变通方法,并为您提供解决此类问题的工具。

    最近一篇关于MySQL服务器组复制的优秀文章: https://www.digitalocean.com/community/tutorials/how-to-configure-mysql-group-replication-on-ubuntu-16-04

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2019-10-15
      • 2017-10-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-02-05
      • 1970-01-01
      • 2018-12-23
      相关资源
      最近更新 更多