【问题标题】:System.Web.Providers.DefaultMembershipProvider having performance issues/deadlocksSystem.Web.Providers.DefaultMembershipProvider 有性能问题/死锁
【发布时间】:2013-04-12 17:24:45
【问题描述】:

我们已经开始使用来自 NuGet 的 Microsoft.AspNet.Providers.Core 包中提供的更新后的 System.Web.Providers。我们开始迁移现有用户,发现性能下降,然后出现死锁。这是少于 30,000 个用户(远少于我们需要创建的 1,000,000+)。当我们调用提供者时,它来自每个服务器上的多个线程,并且有多个服务器运行相同的进程。这是为了能够尽快创建我们需要的所有用户,并模拟我们希望在它上线时看到的负载。

SQL Server 为死锁生成的日志包含以下 EF 生成的 sql:

SELECT
    [Limit1].[UserId] AS [UserId]
  , [Limit1].[ApplicationId] AS [ApplicationId]
  , [Limit1].[UserName] AS [UserName]
  , [Limit1].[IsAnonymous] AS [IsAnonymous]
  , [Limit1].[LastActivityDate] AS [LastActivityDate]
FROM
    (SELECT TOP (1)
        [Extent1].[UserId] AS [UserId]
      , [Extent1].[ApplicationId] AS [ApplicationId]
      , [Extent1].[UserName] AS [UserName]
      , [Extent1].[IsAnonymous] AS [IsAnonymous]
      , [Extent1].[LastActivityDate] AS [LastActivityDate]
     FROM
        [dbo].[Users] AS [Extent1]
        INNER JOIN [dbo].[Applications] AS [Extent2] ON [Extent1].[ApplicationId] = [Extent2].[ApplicationId]
     WHERE
        ((LOWER([Extent2].[ApplicationName])) = (LOWER(@p__linq__0)))
        AND ((LOWER([Extent1].[UserName])) = (LOWER(@p__linq__1)))
    ) AS [Limit1]

我们手动运行查询,执行计划表明它正在执行表扫描,即使存在基础索引。原因是使用了LOWER([Extent1].[UserName])

我们查看了提供程序代码,看看我们是否做错了什么,或者是否有办法拦截或替换数据库访问代码。我们没有看到执行此操作的任何选项,但我们确实找到了LOWER 问题的根源,.ToLower() 在列和参数上都被调用。

return (from u in ctx.Users
    join a in ctx.Applications on u.ApplicationId equals a.ApplicationId into a
    where (a.ApplicationName.ToLower() == applicationName.ToLower()) && (u.UserName.ToLower() == userName.ToLower())
    select u).FirstOrDefault<User>();

有谁知道我们将提供者的行为更改为不使用 .ToLower() 从而允许使用索引的方法?

【问题讨论】:

  • 您好,我现在正在解决同样的问题。你找到好的解决方案了吗?不幸的是,我不能退回到@Mulvihic 建议的 System.Web.Security.SqlMembershipProvider,因为我们需要支持 Azure。

标签: asp.net asp.net-membership membership-provider


【解决方案1】:

你可以根据Sql Server : Lower function on Indexed Column在lower(username)上创建一个索引

ALTER TABLE dbo.users ADD LowerFieldName AS LOWER(username) PERSISTED
CREATE NONCLUSTERED INDEX IX_users_LowerFieldName_  ON dbo.users(LowerFieldName)

【讨论】:

  • 这根本解决不了问题。仍然必须修改查询才能使用这个新的计算列
  • 是的,我测试过,虽然我是新的,但它不起作用。如果我们不修改代码,它将如何开始使用新列。我打算用我自己的自定义提供程序覆盖默认提供程序,该提供程序将与存储过程一起使用。我将在 SQL Server Profiler 的帮助下提取当前提供程序的 SQL 查询。
  • 这个技巧对我来说适用于新旧版本的会员资格。这很讨厌,但 SQL Server 将使用持久索引。
【解决方案2】:

我也在使用 System.Web.Providers.DefaultMembershipProvider 成员资格提供程序,但发现它真的很慢。我更改为 System.Web.Security.SqlMembershipProvider 并发现它要快得多(快 5 倍以上)。

本教程向您展示如何设置您需要使用 SqlMembershipProvider http://weblogs.asp.net/sukumarraju/archive/2009/10/02/installing-asp-net-membership-services-database-in-sql-server-expreess.aspx 的 SQL 数据库

这个自动生成的数据库使用存储过程,这对您的数据库人员来说可能是也可能不是问题。

【讨论】:

  • 由于我们开始遇到的性能问题,我们正在远离它。