0

我有很多文件放在随机文件共享上。我必须将它们复制到我的 SQL Server 2008 数据库中并总结所有要点。将文件从网络复制到 C# 到数据库的开销使得这个过程很慢,而且我有数千个非常大的文件要处理。

文件 1 示例

Player | Points
---------------
Bean   | 10
Ender  | 15

文件 2 示例

Player | Points
---------------
Ender  | 20
Peter  | 5

结果

Player | Points
---------------
Bean   | 10
Ender  | 35
Peter  | 5

当前方法:使用C#,将每个文件读入数据库并合并到主表中。

MERGE INTO Points as Target
USING Source as Source
 ON Target.Player = Source.Player
WHEN MATCHED THEN
  UPDATE SET Target.Points = Target.Points + Source.Points
WHEN NOT MATCHED THEN 
  INSERT (Player, Points) VALUES (Source.Player, Source.Points);

这种方法很好,但我正在寻找改进的想法(有点慢)。

建议的解决方案:

将每个文件读入 SQLite 数据库(基于读取,这应该非常快),将整个数据库批量加载到我的 SQL Server 数据库中并在那里进行所有处理。我应该能够为每个玩家分配一个排名,从而加快分组速度,因为我不是基于文本列进行比较。提议的解决方案的缺点是它不能在多个线程上工作。

将所有这些文件放入数据库并聚合它们的最快方法是什么?

编辑:关于我忘记提及的文件的更多背景信息

  • 这些文件位于多台服务器上
  • 我需要将这项任务的“影响”降到最低——所以不要安装应用程序
  • 文件可能很大- 每个文件多达 1gb,因此不能在内存中执行任何操作
  • 有数千个文件需要处理
4

1 回答 1

1

因此,如果您不能/不想在包含这些文件的各个服务器上运行代码来启动解析操作,并且传输它们的 gigs 和 gigs 很慢,那么这是否是多线程的可能无关紧要 - 性能您过程中的瓶颈是文件传输。

所以做一些假设:

  1. 有一个主服务器,只有它可以工作。

  2. 它可以立即(如果很慢)访问所有必要的文件共享,可以通过简单的路径访问,并且您知道这些路径。

  3. 主计分服务器上有一个本地数据库来存储玩家得分。

如果您可以像传输一个文件一样快地传输多个文件,我会编写执行以下操作的代码:

  1. 收集需要聚合的文件列表——这至少应该是一个小而便宜的列表。将它们收集到一个ConcurrentBag中。

  2. 启动尽可能多的任务,因为机器上的带宽将允许您运行复制操作。您需要进行测试以确定这是什么。

  3. 每个 Task 都将 ConcurrentBag 作为参数。它从一个运行 TryTake() 的循环开始,直到它成功 - 一旦它成功地从包中删除了一个文件路径,它就开始直接从文件位置读取并解析,将每个用户的分数添加到该用户当前在本地数据库中的任何内容中。

  4. 一旦任务完成对文件的处理,它就会继续尝试从 ConcurrentBag 获取下一个文件路径,依此类推。

  5. 最终,所有文件路径都已处理完毕,任务结束。

所以代码大致是:

public void Start()
{
    var bag = new ConcurrentBag<string>();

    for(var i = 0; i < COPY_OPERATIONS; i++)
    {
        Task.Factory.StartNew(() =>
        {
            StartCopy(bag);
        });
    }
}

public void StartCopy(ConcurrentBag<string> bag)
{
    while (true)
    {
        // Loop until the bag is available to hand us a path to work on
        string path = null;
        while (!bag.IsEmpty && !bag.TryTake(out path))
        {}

        // Access the file via a stream and begin parsing it, dumping scores to the db
    }
}

通过流式传输,您可以使复制操作保持全速运行(实际上,操作系统很可能会为您提前读取一点,以真正确保您最大限度地提高复制速度),并且仍然避免因这些文件的大小而破坏内存。

通过不使用多个中间步骤,您可以跳过传输和考虑所有数据的重复成本 - 这样您只需执行一次。

通过使用上述方法,您可以轻松计算最佳复制操作数。

您可以在此处进行一些优化,使其易于重新启动,例如让所有线程接收到一个信号以停止他们正在做的事情并在数据库中记录他们正在处理的文件,他们现在正在处理的文件,以及行他们离开了。您可以让他们以很小的性能成本不断地将这些值写入数据库,以使其防崩溃(通过将行号和分数写入单个事务的一部分)。


原始答案

您忘记在问题中指定这一点,但这些分散的文件似乎记录了玩家在一组网络服务器上玩游戏的得分?

这听起来像是一个令人尴尬的并行问题。与其从每台机器上复制大量文件,不如编写一个可以在所有机器上运行的简单应用程序并将其分发给它们?它只是将机器上的积分相加,然后通过网络向每个玩家发送一个数字和玩家 ID,从而解决网络缓慢的问题。

如果这是一项持续的任务,您可以为总和加上时间戳,这样您就不会重复计算同一点,只需定期批量运行即可。

我会将网络服务器应用程序编写为一个简单的网络应用程序,它只响应一个 IP(您最初要在其上执行所有操作的主计数服务器),并响应请求,在本地运行计数并以总和响应。这样,主服务器只需将请求发送到所有分数服务器,并等待它们发回它们的总和。完毕。

您可以通过将总和数据存储在内存中作为将播放器 id 映射到总和的字典来保持客户端应用程序非常简单 - 无需 SQL。

计数软件还可以在 RAM 中完成所有操作,然后将其全部转储到 SQL Server,以节省时间。

有趣的问题。

于 2013-03-31T18:46:34.057 回答