【问题标题】:How to optimize this AspNetIdenty/Entity Framework generated query?如何优化这个 AspNetIdenty/Entity Framework 生成的查询?
【发布时间】:2020-07-18 04:00:52
【问题描述】:

当我们查看我们的数据库时,这个查询正在消耗大量资源:

(@p__linq__0 nvarchar(4000),@p__linq__1 nvarchar(4000))SELECT 
    [Project1].[Id] AS [Id], 
    [Project1].[Email] AS [Email], 
    [Project1].[EmailConfirmed] AS [EmailConfirmed], 
    [Project1].[PasswordHash] AS [PasswordHash], 
    [Project1].[SecurityStamp] AS [SecurityStamp], 
    [Project1].[PhoneNumber] AS [PhoneNumber], 
    [Project1].[PhoneNumberConfirmed] AS [PhoneNumberConfirmed], 
    [Project1].[TwoFactorEnabled] AS [TwoFactorEnabled], 
    [Project1].[LockoutEndDateUtc] AS [LockoutEndDateUtc], 
    [Project1].[LockoutEnabled] AS [LockoutEnabled], 
    [Project1].[AccessFailedCount] AS [AccessFailedCount], 
    [Project1].[UserName] AS [UserName]
    FROM ( SELECT 
        [Extent1].[Id] AS [Id], 
        [Extent1].[Email] AS [Email], 
        [Extent1].[EmailConfirmed] AS [EmailConfirmed], 
        [Extent1].[PasswordHash] AS [PasswordHash], 
        [Extent1].[SecurityStamp] AS [SecurityStamp], 
        [Extent1].[PhoneNumber] AS [PhoneNumber], 
        [Extent1].[PhoneNumberConfirmed] AS [PhoneNumberConfirmed], 
        [Extent1].[TwoFactorEnabled] AS [TwoFactorEnabled], 
        [Extent1].[LockoutEndDateUtc] AS [LockoutEndDateUtc], 
        [Extent1].[LockoutEnabled] AS [LockoutEnabled], 
        [Extent1].[AccessFailedCount] AS [AccessFailedCount], 
        [Extent1].[UserName] AS [UserName]
        FROM [dbo].[AspNetUsers] AS [Extent1]
        WHERE (@p__linq__0 IS NULL) OR (( CAST(LEN(@p__linq__0) AS int)) = 0) OR (( CAST(CHARINDEX(UPPER(@p__linq__1), UPPER([Extent1].[UserName])) AS int)) > 0)
    )  AS [Project1]
    ORDER BY row_number() OVER (ORDER BY [Project1].[UserName] ASC)
    OFFSET 0 ROWS FETCH NEXT 2147483647 ROWS ONLY

查询计划https://www.brentozar.com/pastetheplan/?id=SyAARIuDU

看起来这是 Entity Framework 为 AspNetIdentity 生成的查询。我找不到与此查询对应的代码。知道如何查找吗?

【问题讨论】:

  • 我严重怀疑 ASP.NET Identity 创建了该查询。这是一个catch-all query,如果它有一个NULL 值,它会试图忽略一个参数。 LINQ 不需要这样的查询,如果您不想要一个,您可以简单地not 添加一个Where 子句。然后查询尝试通过转换为大写来比较字符串,这在使用大小写不变的排序规则时毫无意义?
  • 您的代码在哪里尝试使用可选参数按名称搜索用户?它在哪里将用户名转换为大写以及为什么?您是否使用区分大小写的排序规则?
  • 我们实际上没有这个成本,我们作为托管服务运行,这个查询可以在两个不同的客户中看到,这就是为什么我怀疑它来自 AspNetIdentity
  • 为不同的客户重复相同的低效代码将产生同样的低效查询。这不是 ASP.NET Identity 的问题。 您的代码在哪里对用户名执行通配符搜索
  • 我不确定您希望通过悬赏这个问题获得什么。 @PanagiotisKanavos 已经提出了所有正确的问题,并告知您查询的问题,并声明它绝对不是由 asp.net 身份生成的默认查询。您需要搜索您的代码库以找出生成此查询的原因,然后相应地优化代码。

标签: sql sql-server performance entity-framework


【解决方案1】:

此代码正在生成此类查询。

var users = _userManager().Users.Where(u => string.IsNullOrEmpty(usernameToMatch) || u.UserName.Contains(usernameToMatch)).OrderBy(u => u.UserName).Skip(pageIndex * pageSize).Take(pageSize);

感谢https://stackoverflow.com/users/134204/panagiotis-kanavos 的正确和坚持。我看错地方了。

要吸取的教训:或实体框架中的条件 - 或一般情况下 - SQL - 是一个坏主意。

【讨论】:

  • OR 绝对没有问题。问题是查询。第一部分不应该存在根本 - string.IsNullOrEmpty(usernameToMatch) 不使用任何表格数据。如果输入为空,则不执行查询。真正的问题是使用Contains 转换为LIKE '%abc%'。这不能使用任何索引。改用StartsWith,它转换为LIKE 'abc%',本质上是一个可以通过索引加速的范围查询
猜你喜欢
  • 2012-07-15
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-01-25
  • 2012-12-22
相关资源
最近更新 更多