使用c#和SQL Server读取和聚合数千个文件

本文关键字:千个 文件 SQL Server 读取 使用 | 更新日期: 2023-09-27 18:06:38

我有很多文件躺在随机文件共享。我必须把它们复制到我的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,所以在内存中做任何事情都不是一个选项
  • 有数千个文件要处理

使用c#和SQL Server读取和聚合数千个文件

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

做一些假设:

  1. 有一个主服务器,只有它做任何工作。

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

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

如果你可以像传输一个文件一样快速地传输多个文件,我会编写这样的代码:

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

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

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

  4. 一旦Task完成对一个文件的处理,它将继续尝试从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,解决了网络速度慢的问题。

如果这是一个正在进行的任务,你可以用时间戳计算总和,这样你就不会两次计算同一个点,只是定期批量运行它。

我写的web服务器应用程序作为一个简单的web应用程序,只响应一个IP(主计数服务器你最初打算做的一切),并在响应一个请求,运行计数本地和响应的总和。这样,主服务器就可以向所有分数服务器发送请求,并等待它们发送回总价。做。

您可以保持客户端应用程序非常简单,只需将sum数据存储在内存中作为字典映射播放器id到sum -不需要SQL。

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

有趣问题。