【问题标题】:Parsing and Analysing few GBs data解析和分析几 GB 的数据
【发布时间】:2013-01-02 03:36:38
【问题描述】:

我正在寻找一种分析自定义日志文件的方法。

我现在已经使用 LINQ 和 C#.NET 实现了。它仅适用于最大 500MB 的日志文件。

日志文件的每一行都被制成一个看起来像

的对象
public class Metrics
{
    public DateTime Date { get; set; }
    public string Metrics1 { get; set; }
    public string Metrics2 { get; set; }
                :
                :
    public string Metrics9 { get; set; }
}

List<Metrics> MetricsList = new List<Metrics>();

MetricsList 已填充。 在 MetricsList 上运行各种 LINQ 查询以提供有用的分析。 据观察,一个 Metrics 对象需要 300 个字节。我在 500MB 的日志文件中有大约 400 万行,这使得仅 MetricsList 的大小就消耗了超过 1GB 的程序内存。

我的要求是解析和分析最大 2 GB 的文件,这看起来会消耗 4 GB 的内存。

使用 Windows、Microsoft 技术和任何开源库的任何更好的方法或替代方案。

【问题讨论】:

  • 您可能需要将数据流式传输。一次读取一个指标(或一大块指标),将它们从内存中丢弃,然后移至下一个。
  • 你读的怎么样?您可以使用StreamreaderFile.ReadLines 逐行读取一行而不是全部读取(例如File.ReadAllLines)。您还应该看看MemoryMappedFile-class内存映射文件使程序员能够处理非常大的文件,因为内存可以同时管理,并且它们允许对文件进行完全、随机的访问,而无需查找。内存映射文件也可以跨多个进程共享​​>
  • “我在使用 Windows 和 Microsoft 技术方面存在限制”,您为什么认为这是限制?您还有哪些其他技术?
  • @Tim 我的意思是为什么张贴者认为 Window 和 Microsoft 技术是有限的。他认为还有哪些其他技术可以不受限制地做到这一点。我想你误解了我的评论!
  • LINQ 只停留在 500MB?您是否在指标中有重复值?如果是这样,请创建一个 Dictionary 并在 List 中存储 int。

标签: c# .net performance memory scalability


【解决方案1】:

我使用 SQlite 完成了类似的任务。安装 System.Data.SQLite NuGet(可选:我也使用 Dapper NuGet 作为一个非常有效的微 ORM),然后你就有了一个非常好的工具来执行查询和生成你的报告。您可能不喜欢的唯一一件事是您必须编写 SQL 而不是 LINQ(虽然 SQLite 也有 LINQ;但我没有使用它)。

这样内存消耗也会消失。

【讨论】:

    【解决方案2】:

    通常您不想将这样的文件存储在内存中(当然除非您有足够的内存),而是在解析文件时处理数据。我只需安装更多内存并将解决方案设置为 64 位可能...

    但是,如果这不是一个选项,您可以随时优化内存使用。 .NET 将字符串存储为 char[] ,其中 char 基本上是 2 字节短。您可以通过使用 Encoding.UTF8.GetBytes 将其存储为 byte[] 而不将其存储为 char[] 来轻松节省大量内存。

    此外,在 64 位环境中,每个字符串或 byte[] 消耗 24 个字节(16 个用于对象本身,8 个用于指针)。如果你有很多小字符串,那可以加起来。除了将它们存储为字符串之外,您还可以存储单个 byte[] 并在 getter 中进行解析。

    所以总结一下我的建议是:购买更多内存或在您阅读/需要时处理数据。

    [更新+1]

    刚刚注意到您使用了一个列表。随用随处理的最简单方法是将文件读取为 IEnumerable 并在其上使用 Linq。不要先把它放在一个列表中。例如:

    public IEnumerable<Metric> ReadFile()
    {
        string s;
        while ((s=myFileReader.ReadLine())!=null)
        {
            yield return Parse(s);
        }
    }
    
    int someAnalysis = ReadFile().Sum((a)=>(a.Metric1.Length)); // or whatever you do
    

    [更新+2]

    哦,我还有另一个技巧要给你。读取文件可能会影响性能,因为文件 IO 相对来说很糟糕。因此,除了使用上面的 IEnumeration 技巧,您还可以使用压缩流将所有数据存储在内存中 - 然后在处理期间使用它而不是文件。

    对于那些想知道我是否认真对待这个奇怪的解决方案的人:这是构建搜索技术和数据库时常用的技术,仅仅是因为拥有更多(快速)内存意味着更少(慢)磁盘 IO。此外,日志文件可能会很好地压缩。

    所以在内存流之上读取文件 && flatestream。然后按照上面讨论的方式为 Linq 读取它(同样,在 memorystream 之上的 flatestream)。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2019-01-06
      • 1970-01-01
      • 2023-04-02
      • 1970-01-01
      • 2018-07-01
      • 2020-07-15
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多