优化ASP.NET 2.0 Profile Provider
你可知道优化ASP.NET 2.0 Profile Provider中有两个能进行优化的重要存储过程吗?如果在没有进行任何必要优化的情况下使用过它们,你的服务器将会因为业务量的增长而变得异常繁忙。这里有一个故事:
在2006年3月份的MIX大会上展示了Pageflakes。当时我们是我们最富有魅力的时候。我们是作为支持Showcase of Atlas Web site的第一家公司。每天访问站点的用户数量接连攀升。有一天我们注意到,数据库服务器不再工作了。然后我们重新启动了服务器,工作恢复了正常,可过了一小时后,服务器再次死掉了。在我们对服务器主体部分进行了检查分析之后,我们发现CPU占用率高达100%并且IO使用率更高。
硬盘驱动器发热,并进行了自动关闭以保护其不受损坏。这对我们来说感到十分惊奇,因为我们原以为我们一直都很聪明,并且针对每个单独的Web服务功能都使用了 profile。因此,我们对上百兆的日志进行了分析希望能找到那个Web服务耗费了这么多时间。我们对其中一个产生了怀凝。它是加载用户页面配置的第一个功能。我们将这个功能分解成了很多小的部分以便我们能快速找到那一部分花费了大部分时间。
- private GetPageflake(string source, string pageID, string userUniqueName)
- {
- if( Profile.IsAnonymous )
- {
- using (new TimedLog(Profile.UserName,"GetPageflake"))
- {
正如你所看到的情形,整个方法主体就是用于计时。如果你想了解这种计时是如何工作的,我会在一篇新的文章中进行解释。我们也对其中一小部分我们怀凝最耗费资源的功能进行了计时。但是在我们的代码中需要花费大量时间处理的部分很多。我们的代码一直都是经过优化的(毕竟,你知道是谁在查看它,就是我)。
同时,用户开始了大叫,管理也开始混乱,支持部门的员工也开始抱怨这么多电话。开发人员搞得焦头乱额此时也变得胡言乱语。这并不是什么特殊情况,仅仅就是一个每个月会遇到2次的一个典型解决方案。
现在你一定在大声叫喊了,“你可以使用SQL Profiler啊,你这个傻瓜!”。问题是我们使用的是SQL Server工作组版本。不支持SQL Profiler这个功能。因此我们不得不采用我们的方法来解决这个问题,无论怎样也得使其在服务器上正常运行。不要问这个到底如何实现。在运行了SQL Profiler以后,孩子,真让我们吃惊!原来才是这个巨大的存储过程dbo.aspnet_Profile_GetProfiles给我们带来了痛苦!
我们习惯大量使用(并且一直使用)Profile provider这个工具。
- CREATE PROCEDURE [dbo].[aspnet_Profile_GetProfiles]
- @ApplicationName nvarchar(256),
- @ProfileAuthOptions int,
- @PageIndexint,
- @PageSize int,
- @UserNameToMatch nvarchar(256) = NULL,
- @InactiveSinceDate datetime = NULL
- AS
- BEGIN
- DECLARE @ApplicationId uniqueidentifier
- SELECT @ApplicationId = NULL
- SELECT @ApplicationIdApplicationId = ApplicationId
- FROM aspnet_Applications
- WHERE LOWER(@ApplicationName)
- = LoweredApplicationName
- IF (@ApplicationId IS NULL)
- RETURN
- -- Set the page bounds
- DECLARE @PageLowerBound int
- DECLARE @PageUpperBound int
- DECLARE @TotalRecords int
- SET @PageLowerBound = @PageSize * @PageIndex
- SET @PageUpperBound = @PageSize - 1 + @PageLowerBound
- -- Create a temp table TO store the select results
- CREATE TABLE #PageIndexForUsers
- (
- IndexId int IDENTITY (0, 1) NOT NULL,
- UserId uniqueidentifier
- )
- -- Insert into our temp table
- INSERT INTO #PageIndexForUsers (UserId)
- SELECT u.UserId
- FROMdbo.aspnet_Users
- u, dbo.aspnet_Profile p
- WHERE ApplicationId = @ApplicationId
- AND u.UserId = p.UserId
- AND (@InactiveSinceDate
- IS NULL OR LastActivityDate
- <= @InactiveSinceDate)
- AND (
- (@ProfileAuthOptions = 2)
- OR (@ProfileAuthOptions = 0
- AND IsAnonymous = 1)
- OR (@ProfileAuthOptions = 1
- AND IsAnonymous = 0)
- )
- AND (@UserNameToMatch
- IS NULL OR LoweredUserName
- LIKE LOWER(@UserNameToMatch))
- ORDER BY UserName
- SELECT u.UserName, u.IsAnonymous, u.LastActivityDate,
- p.LastUpdatedDate, DATALENGTH(p.PropertyNames)
- + DATALENGTH(p.PropertyValuesString)
- + DATALENGTH(p.PropertyValuesBinary)
- FROMdbo.aspnet_Users
- u, dbo.aspnet_Profile p, #PageIndexForUsers i
- WHERE
- u.UserId = p.UserId
- AND p.UserId = i.UserId
- AND i.IndexId >= @PageLowerBound
- AND i.IndexId <= @PageUpperBound
- DROP TABLE #PageIndexForUsers
- END
- END
以上是优化ASP.NET 2.0 Profile Provider。
【编辑推荐】