如何通过实体框架从 C# 中的同步代码并行运行查询

Posted

技术标签:

【中文标题】如何通过实体框架从 C# 中的同步代码并行运行查询【英文标题】:How to run in parallel a query from synchronous code in C# via Entity Framework 【发布时间】:2022-01-09 03:24:33 【问题描述】:

我的目标是加快查询速度,我想利用并行性,假设我在 ids 列表中有 2,000 个项目,我将它们分成 4 个列表,每个列表有 500 个 id,我想打开 4 个踏板每个人都将创建一个数据库调用并统一他们的结果,以实现我使用 Parallel.ForEach,但它并没有提高查询的性能,因为它显然不适合 io 绑定操作:Parallel execution for IO bound operations

if 块中的代码对每个使用并行,而 else 块中的代码在常规 foreach 中执行。

问题是包含这个查询的方法不是异步的(因为它是在一个非常遗留的组件中)并且它不能更改为异步,基本上我想在非异步方法中进行并行 io 绑定计算(通过实体框架)。

实现这一目标的最佳做法是什么?我看到也许我可以为此使用Task.WaitAll(),我不在乎阻塞运行此查询的线程,我更担心从非异步方法调用的Task.WaitAll() 会出现问题

我使用实体框架作为 SQL 数据库上的 ORM,我为每个线程打开一个单独的上下文,因为上下文不是线程安全的。

也许我使用的锁是导致我出现问题的锁,我可以将其更改为ConcurrentDictionary

下面代码中描述的场景是我需要改进的场景的简化,在我们的实际应用程序中,我确实需要在加载 id 后读取相关实体,并对它们执行复杂的计算。

代码:

//ids.Bucketize(bucketSize: 500) -> split one big list, to few lists each one with 500 ids 
IEnumerable<IEnumerable<long>> idsToLoad = ids.Bucketize(bucketSize: 500);
if (ShouldLoadDataInParallel())

    object parallelismLock = new object();
    
    Parallel.ForEach(idsToLoad,
        new ParallelOptions  MaxDegreeOfParallelism = 4 ,
        (IEnumerable<long> bucket) =>
        
        List<long> loadedIds = GetIdsQueryResult(bucket);

            lock (parallelismLock)
            
                allLoadedIds.AddRange(loadedIds );
            
        );

else

    foreach (IEnumerable<long> bucket in idsToLoad)
    
        List<long> loadedIds = GetIdsQueryResult(bucket);

        allLoadedIds.AddRange(loadedIds);
    

【问题讨论】:

你没有。您改为修复数据访问代码。使用 EF Core 或任何 ORM 按 ID 执行批量删除毫无意义。本案例不涉及任何对象 此外,并行执行批处理操作只会增加阻塞,不会减少阻塞。您仍然使用相同的磁盘、相同的 CPU、相同的事务日志。 你需要执行的SQL是DELETE SomeTable where ID in (...)。 EF Core 无法做到这一点,但 Dapper 可以。您可以在 DbContext 的连接上使用 Dapper 来执行例如Execute("delete theTable where ID in (@ids):, new ids=someList")。重复执行以删除批次而不会淹没事务日志 the delete was just to simplify the use case, 在这种情况下,问你真正的问题并描述真正的问题。细节很重要。唯一可以确定的是“并行化”只会损害性能。通常呈指数增长 此外,如果数据来自数据库,您可以修改SELECT 查询以实际删除匹配的行。没有理由将数据拉到客户端只是为了发回 DELETE。 【参考方案1】:

[并行运行多个查询]的最佳做法是什么?

带有单独 DbContext/SqlConnection 的 Parallel.ForEach 是一种很好的方法。

只是并行运行查询在这里并没有真正的帮助。

如果您的 4 个查询针对 4 个单独的数据库,那么您可能会获得不错的改进。但是,在单个实例上并行运行 4 个单独的查询可能不会比运行单个大型查询快,原因有很多。其中包括阻塞、资源争用、服务器端查询并行性以及查询之间的重复工作。

所以

我的目标是加快查询速度,我想利用并行性

因此,这通常不是加快查询速度的好方法。但是,有许多加快查询速度的好方法,所以如果您发布一个新问题,其中包含查询的详细信息以及一些示例数据,您可能会得到一些更好的建议。

【讨论】:

以上是关于如何通过实体框架从 C# 中的同步代码并行运行查询的主要内容,如果未能解决你的问题,请参考以下文章

如何在 C# 和 WPF 中的实体框架中创建数据库和表?

如何动态构建实体框架查询?

如何使用 C# 代码部分中的 SQL 函数使用 MVC 4 中的实体框架

使用实体框架和存储过程进行并发检查

linq中的算术运算使用实体框架mvc C#

跟踪从实体框架代码调用的查询