プログラミング
EF Core 11でSplit Queryが高速化
EF Core 11 makes your split queries faster (steven-giesel.com)
要約
EF Core 11では、`AsSplitQuery`機能におけるパフォーマンスの改善が行われました。以前のバージョンでは、参照ナビゲーションを含めるとコレクションクエリで不要なJOINが発生し、クエリが非効率になることがありました。EF Core 11では、これらの参照ナビゲーションをクエリから除外することで、SQLクエリが最適化され、実行速度の向上とメモリ使用量の削減が実現されています。
全文翻訳
目次
コードベースのどこかでAsSplitQueryを使用している場合、EF Core 11にはプレゼントがあります。それは、クエリが高速になることです。
デフォルトでは、EF Coreはすべてのデータを1つのクエリでロードします。コレクションをIncludeすると、それはJOINを意味し、コレクションに対するJOINは親行を子ごとに重複させます。より多くのものをJOINするほど、「悪化」します。
AsSplitQueryはそれを解決します。EFはルートエンティティ用のクエリと、追加のコレクションごとのクエリを実行します。
もちろん、これは万能薬ではなく、以下のような結果を招く可能性があります。
より多くのラウンドトリップ
ルートエンティティと子エンティティの間で状態が変更される可能性(複数のトランザクションであるため、アトミックな操作ではありません)。
詳細はこちら: https://learn.microsoft.com/en-us/ef/core/querying/single-split-queries
では、問題は何でしょうか?元の問題からのセットアップは次のとおりです。1つの参照ナビゲーション、1つのコレクションナビゲーションです。
var result = context.Blogs
.Include(x => x.BlogType) // 参照ナビゲーション
.Include(x => x.Posts) // コレクションナビゲーション
.AsSplitQuery()
.ToList();
EF Core 10まで、これは2つのクエリを生成していました。
SELECT [b].[Id], [b].[BlogDetailId], [b].[BlogTypeId], [b].[Name], [b0].[Id], [b0].[Type]
FROM [Blogs] AS [b]
LEFT JOIN [BlogType] AS [b0] ON [b].[BlogTypeId] = [b0].[Id]
ORDER BY [b].[Id], [b0].[Id]
SELECT [p].[Id], [p].[BlogId], [p].[Title], [b].[Id], [b0].[Id]
FROM [Blogs] AS [b]
LEFT JOIN [BlogType] AS [b0] ON [b].[BlogTypeId] = [b0].[Id]
INNER JOIN [Post] AS [p] ON [b].[Id] = [p].[BlogId]
ORDER BY [b].[Id], [b0].[Id]
2番目のクエリを見てください。Postsを取得するクエリです。なぜBlogTypeが含まれているのでしょうか?テーブルをJOINし、そのIdを選択し、それに基づいてORDER BYしています。これらはどれも必要ありません。PostをBlogに一致させるには、Blogの主キーがあれば十分です。
そして、参照ナビゲーションが増えるほど悪化します。参照ナビゲーションに5つのIncludeがあるとどうなるでしょうか?すべてのコレクションクエリは、5つのJOINと5つの追加のORDER BY列を引きずります。データベースは作業を行い、結果を破棄し、クエリプランに悪影響を与えます。
では、何が変わったのでしょうか?EF Core 11(プレビュー3以降)は、コレクションクエリから参照ナビゲーションを削除します。2番目のクエリは、手動で記述したであろうものになります。
SELECT [p].[Id], [p].[BlogId], [p].[Title], [b].[Id]
FROM [Blogs] AS [b]
INNER JOIN [Post] AS [p] ON [b].[Id] = [p].[BlogId]
ORDER BY [b].[Id]
ベンチマーク
このブログ投稿の最後に、セットアップ全体のリンクを掲載します。ここでは、私が使用していた小さなセットアップを示します。上記のリンクされたGithubイシューで、ほぼ同じものを見つけることができます。
免責事項:EF 10(.net 10内)とEF 11(.net 11内)を比較しました。とはいえ、すべてのメリットがEF 11のみに起因するわけではない可能性があります。ベンチマークで2つの変数を変更したためです。理論的には、.net 10から.net 11への変更のみである可能性もありますが、可能性は低いです。
[MemoryDiagnoser]
[SimpleJob(RuntimeMoniker.Net10_0)]
[SimpleJob(RuntimeMoniker.Net11_0)]
public class SplitQueryBenchmarks
{
private SqliteConnection _connection = null!;
private DbContextOptions<BloggingContext> _options = null!;
[GlobalSetup]
public void Setup()
{
_connection = new SqliteConnection("Data Source=:memory:");
_connection.Open();
_options = new DbContextOptionsBuilder<BloggingContext>()
.UseSqlite(_connection)
.Options;
using var context = new BloggingContext(_options);
context.Database.EnsureCreated();
Seeder.Seed(context, blogCount: 5_000, postsPerBlog: 5);
}
[GlobalCleanup]
public void Cleanup()
=> _connection.Dispose();
[Benchmark]
public List<Blog> SplitQuery()
{
using var context = new BloggingContext(_options);
return BlogQuery(context).AsSplitQuery().ToList();
}
private static IQueryable<Blog> BlogQuery(BloggingContext context)
=> context.Blogs
.AsNoTracking()
.Include(b => b.Author)
.Include(b => b.Category)
.Include(b => b.Series)
.Include(b => b.Posts);
}
結果:
| Method | Runtime | Mean | Error | StdDev | Median | Gen0 | Gen1 | Gen2 | Allocated |
|----------- |---------- |---------:|---------:|---------:|---------:|----------:|----------:|---------:|----------:|
| SplitQuery | .NET 10.0 | 68.53 ms | 1.358 ms | 2.742 ms | 67.54 ms | 4375.0000 | 1000.0000 | 375.0000 | 33.35 MB |
| SplitQuery | .NET 11.0 | 62.07 ms | 1.213 ms | 2.788 ms | 61.10 ms | 4111.1111 | 1333.3333 | 444.4444 | 30.15 MB |
実行時間と割り当てが減少しました!
リソース
このブログ投稿のソースコード:こちら
私のサンプルコードはすべてこのリポジトリでホストされています:こちら
Githubイシュー:https://github.com/dotnet/efcore/issues/29182
1 コピー
LinkedInで共有
Xで共有
Blueskyで共有
スポンサー
関連記事
EF Core 6 - 新機能
EF Core 6 はもうすぐ登場し、非常に有望に見えます。このブログ投稿では、どのような新機能が登場するかを紹介します。
7/12/2021 3 分の読み込み
EF Core
C#
.NET 6
EF Core 6
私のEF LINQ to SQLクエリは安全ですか?
EF LINQクエリがSQLインジェクション攻撃に対して脆弱かどうか、疑問に思ったことはありませんか?ユーザーデータをテキストフィールドからクエリしているか、APIがデータベースにヒットしたものを直接取得しているかのどちらかです。
6/17/2024 3 分の読み込み
C#
.NET
Entity Framework
EF
.NET 11でのZstandard圧縮
.NETには、DeflateStream、GZipStream、ZLibStream、BrotliStreamがしばらく前からありました。.NET 11では、新しいものがパーティーに参加します。ZstandardStreamです。そして今、私たちは.NETで「Zstd」と言うことができます。
5/18/2026 2 分の読み込み
.NET
.NET 11