【问题标题】:(Entity framework) Group by - low performance(实体框架)Group by - 低性能
【发布时间】:2016-03-26 10:05:29
【问题描述】:

我对实体框架的性能有一个非常特殊的问题。我将框架的第 7 版与 SQLite 提供程序一起使用(均来自 nuget)。数据库有大约 1000 万条记录,但未来将有大约 1 亿条记录。 db的构建很简单:

public class Sample
{
    public int SampleID { get; set; }
    public long Time { get; set; }
    public short Channel { get; set; } /* values from 0 to 8191, in the presented test 0-15 */
    public byte Events { get; set; } /* 1-255 */
}

public class Channel
{
    public int ChannelID { get; set; }
    public short Ch { get; set; }
    public int Es { get; set; }
}

public class MyContext : DbContext
{
    // This property defines the table
    public DbSet<Sample> Samples { get; set; }
    public DbSet<Channel> Spectrum { get; set; }

    // This method connects the context with the database
    protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
    {
        var connectionStringBuilder = new SqliteConnectionStringBuilder { DataSource = "E://database.db" };
        var connectionString = connectionStringBuilder.ToString();
        var connection = new SqliteConnection(connectionString);

        optionsBuilder.UseSqlite(connection);
    }
}

我尝试按频道对事件进行分组,并将它们汇总为频谱之类的东西。当我使用 linq2sql 时,我的性能非常低。对于 10m 的记录,查询大约需要 15 分钟并获得大约 1 GB 的 RAM,然后抛出 OutOfMemoryException - 我认为 Entity Framework 正在将所有记录作为对象加载到内存中 - 但为什么呢?另一方面,简单的 SQL 大约需要 3 秒,并且不会占用大量 RAM。

        using (var db = new MyContext())
        {
            var res1 = from sample in db.Samples
                       group sample by sample.Channel into g
                       select new { Channel=g.Key, Events = g.Sum(s => s.Events) };
            res1.ToArray();

            var res2 = db.Natas.FromSql("SELECT Channel as ChannelID, Channel as Ch, SUM(Events) as Es FROM Sample GROUP BY Channel");
            var data = res2.ToArray();
        }

有什么建议吗?感谢您的帮助;)

【问题讨论】:

  • 为什么需要'orderby'?
  • 我使用 order by 以正确的顺序获得结果(也许我应该在分组后使用它) - 在这种情况下它并不重要 - 删除这部分 linq 后,性能仍然相同。我删除了问题代码中的 OrderBy 部分,以使其更清晰。
  • 但 order by 不在“更快的纯 sql”中。除非 SQL 在功能上相同,否则您将苹果与橙子进行比较。
  • 你检查过EF生成并发送到数据库的查询吗?
  • 最新版本中 EF 的 SQLite 提供程序在显示 SQL 时出现问题;/ - 这就是我决定在这里提出这个问题的原因。

标签: c# performance entity-framework sqlite linq-to-sql


【解决方案1】:

建议?忽略实体框架。

如:这完全不是 EF 问题,甚至都不好笑。

查看EF发出的SQL,然后从那个层面进行优化。哦,你对 SQL 影响不大;但是对于这样一个简单的语句,SQL 将是最佳的。

数据库不是最佳的——而且有一个提示你从未看过 SQL。有指数吗?代码优先的惊人之处在于它对数据库的复杂性一无所知,您需要首先从“我的数据库是否最佳”来看待它。指数。而且 - 可悲的是 - 硬件。如果达到 1 亿行,则需要数据库中的强大功能来处理。

我认为 Entity Framework 正在将所有记录作为对象加载到内存中 - 但为什么呢?

性能调试中的规则 1:不要思考 - 检查。查看生成的 SQL(日志,res1 变量可以显示)并查看提交到数据库的内容。

您可能只有这么多数据。你没有说有多少频道存在 - 这可能需要更大的机器。

检查一下。

另外:除非您需要,否则将结果放入数组中并不是很聪明。在这种情况下,数组存在内存问题(重新分配以获取大小),而 LIST 可能更好(使用更多内存但不需要重新分配)。不过,一般来说,您希望避免具体化结果集 - 即从可枚举中工作。并非总是如此,但您的测试可能会简单地显示那方面的问题。结果数组可能很大。并且需要一块内存。

请认真地质疑您选择的数据库技术。 SqlLite 很好——它很小,很轻。它在记忆中。它不适合处理大量数据,它不是一个完整的数据库服务器。使用 Sql Express 可能会好得多(如果有的话:SQL express 将使用内存来缓存不在您的进程中而是单独的)。我个人不会将 SqlLite 用于可能使用数亿条记录的东西。

另外:请注意您的 SQL 是不同的。 EF 部分有一个 OrderBy(不需要),SQL 没有。订购可能很昂贵。这让我们回到“获取实体框架生成的 SQL”。

【讨论】:

  • 我尝试检查生成的 SQL。不幸的是,实际版本的 EF 在使用 SQLite 提供程序时显示 SQL 存在一些问题。所以我没有能力检查它。我查看了 Visual Studio 中的 RAM 使用率图表,发现使用率仍在上升。我在单个文件(SQLite)和二进制文件中的数据库之间有简单的选择。我的经验表明,引擎会比我的解决方案更好地优化 db。该程序将用于物理实验,要求解决方案必须在单个文件中。
  • 查询结果最多有 8192 行(在测试的场景中只有 16 行)。 orderby 语句没有影响(选中)。所有数据都将用于生成图表,因此没有延迟加载的空间。
  • 好吧,这给你留下了一个问题。对于这样的东西 - 考虑不使用 EF。一个不错的选择是 Linq2DB,它是“半 ORM”。它做了很多——而且更好——从 sql 到 LINQ 的映射,但没有更高的功能。但是它非常适合报告,速度超级快。您可能有 SqlLite 问题。我建议,作为下一步,然后使用 - 内存分析器来查看内存丢失的位置。并尝试制作 ToList THEN ToArray,这在内存限制下可能更有效(无需调整大小)。
  • 谢谢。老实说,我选择 EF 是因为我看到了学习这些东西的机会。但我当然会尝试提到 Linq2DB。
【解决方案2】:

问题与 SQLite 提供程序有关。更改为 SQL Server Compact 后一切正常;)

【讨论】:

  • 这个答案需要更详细地描述所做的更改。
猜你喜欢
  • 2013-02-02
  • 2012-09-10
  • 2017-06-14
  • 1970-01-01
  • 2010-12-23
  • 1970-01-01
  • 2013-12-05
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多