Проблемы с производительностью ядра EF QueryFilter - PullRequest
4 голосов
/ 19 марта 2019

В нашей системе мы столкнулись с проблемами производительности при использовании QueryFilters в ядре EF. Проблема заключается в том, что ядро ​​EF фильтрует внутри LEFT JOIN вместо того, чтобы выполнять фильтрацию вне него.

Сгенерированный SQL выглядит примерно так:

SELECT [pom].[Id],
        [pom].[DeleteDate],
        [pom].[UpdateDate],
        [pom].[Version],
        [t].[Id],
        [t].[MandatorId],
        [t].[NetPriceAmount],
        [t].[NetPriceCurrencyIso4217Code]
FROM [externaldata].[PurchaseOfferMetadata] AS [pom]
    LEFT JOIN (
    SELECT [po].[Id],
            [po].[MandatorId],
            [po].[NetPriceAmount],
            [po].[NetPriceCurrencyIso4217Code]
    FROM [externaldata].[PurchaseOffer] AS [po]
    WHERE [po].[MandatorId] = 1
) AS [t] ON [pom].[Id] = [t].[Id]
WHERE [pom].[Id] IN 
    (CAST(3094411 AS bigint), 
    CAST(4757070 AS bigint), 
    CAST(4757112 AS bigint), 
    CAST(5571232 AS bigint))

Проблемная часть - WHERE [po].[MandatorId] = 1. Если это будет во втором операторе WHERE, запрос выполняется намного быстрее.

Модель базы данных настроена так:

modelBuilder.Entity<PurchaseOffer>()
      .HasQueryFilter(po => po.MandatorId == 1)
      .ToTable(nameof(PurchaseOffer), schema: ExternalDataSchemaName);

modelBuilder.Entity<PurchaseOfferMetadata>()
      .HasOne(pom => pom.PurchaseOffer)
      .WithOne(po => po.Metadata)
      .HasForeignKey<PurchaseOffer>(po => po.Id);

В базе данных мы установили внешний ключ так:

IF OBJECT_ID('[externaldata].[FK_PurchaseOffer_PurchaseOfferMetadata]', 'F') IS NULL
BEGIN
    ALTER TABLE [externaldata].[PurchaseOffer] ADD CONSTRAINT [FK_PurchaseOffer_PurchaseOfferMetadata] FOREIGN KEY
    (
        [Id]
    )
    REFERENCES [externaldata].[PurchaseOfferMetadata] ([Id])
END;

Основной запрос EF выглядит следующим образом:

var existingPurchaseOfferMetadatasById = await db.PurchaseOfferMetadatas
   .Where(pom => purchaseOfferIds.Contains(pom.Id))
   .Include(pom => pom.PurchaseOffer)
   .ToDictionaryAsync(pom => pom.Id, cancellationToken);

В настоящее время в каждой таблице имеется следующее количество записей:

  • Покупка предложения метаданных: 12'654'639
  • Предложение о покупке: 1'689'634

Кто-нибудь также сталкивался с этой проблемой и мог бы найти решение этой проблемы?

1 Ответ

0 голосов
/ 14 июня 2019

EF исторически не проделала большую работу по созданию наиболее оптимизированных SQL-запросов, когда соединения сложны. Я не могу ответить, как вы получаете Linq, чтобы понять ваш дизайн оптимизации базы данных; однако, если вы знаете SQL, который должен быть сгенерирован, то я предлагаю для лучшей комплексной производительности READ использовать пакет NuGet, такой как Dapper , чтобы написать точный SQL, который вы хотите, и получить соединения, которых заслуживает ваша база данных.

using Dapper;

[...]

public class SalsaZima
{
  public int ID { get; set; }
  public string MySalsaColumn { get; set; }
  public string MyZimaColumn { get; set; }
}

[...]

// get data from database
using (IDbConnection dp = Dapper)
{
  string query = @"SELECT 
                     s.ID
                    ,s.MySalsaColumn
                    ,z.MyZimaColumn
                   FROM dbo.Salsa s 
                   LEFT JOIN dbo.Zima z
                     ON s.ZimaID = z.ID";

  return dp.Query<SalsaZima>(query).ToList();
}
...