缓慢地对非常大的SQL Server表进行许多聚合查询

问题描述

| 我有一个自定义的日志/事务表,该表可跟踪用户Web应用程序中的每个操作,它目前有数百万条记录,并且每分钟都有增长。在我的应用程序中,我需要在sql中实现一些预先计算用户活动/动作的方法,以确定应用程序中用户是否可以使用其他功能/动作。例如,在页面加载之前,我需要检查用户是否查看了X次页面
(SELECT COUNT(*) FROM MyLog WHERE UserID = xxx and PageID = 123)
我正在使用联接进行几个类似的聚合查询,以检查其他条件,并且性能很差。这些检查针对每个页面请求进行,并且应用程序每分钟可以接收数百个请求。 我正在寻找通过sql和/或应用程序代码来提高应用程序性能的任何想法。 这是一个.NET 2.0应用程序,并使用sql Server 2008。 提前非常感谢!     

解决方法

最简单的方法是自己将计数存储在表中。然后,在添加记录时(希望通过SP),您可以简单地在聚合表中增加受影响的行。如果您真的担心计数过高,可以在明细表上放置一个触发器以更新聚合表,但是我不喜欢触发器,因为它们的可见性很小。 另外,这些计数需要如何更新?这可以每天存储一次吗?     ,像这样查询日志表可能会比较麻烦,但值得。 作为一种替代方案,我建议根据需要使用类似memcache的值来存储值。只要您在每次命中时更新高速缓存,它就会大大加快查询大型数据库表的速度。 Memcache有一个内置的增量运算符来处理这种事情。 这样,您只需要在第一次访问时查询数据库。 另一种选择是使用预先计算的表,并根据需要对其进行更新。     ,您是否在UserID和PageID上索引MyLog?如果没有,那应该给您带来巨大的收益。     ,由于执行的操作数量众多,Todd很难做到这一点。 您是否已检查该数据库上的索引? 您可以执行以下存储过程,以帮助至少找到有效的索引。我不记得在哪里找到了它,但是它帮助了我:
CREATE PROCEDURE [dbo].[SQLMissingIndexes]
@DBNAME varchar(100)=NULL
AS
BEGIN
    -- SET NOCOUNT ON added to prevent extra result sets from
    -- interfering with SELECT statements.
    SET NOCOUNT ON;

    SELECT 
        migs.avg_total_user_cost * (migs.avg_user_impact / 100.0) 
        * (migs.user_seeks + migs.user_scans) AS improvement_measure,\'CREATE INDEX [missing_index_\' 
        + CONVERT (varchar,mig.index_group_handle) 
        + \'_\' + CONVERT (varchar,mid.index_handle) 
        + \'_\' + LEFT (PARSENAME(mid.statement,1),32) + \']\'
        + \' ON \' + mid.statement 
        + \' (\' + ISNULL (mid.equality_columns,\'\') 
        + CASE WHEN mid.equality_columns IS NOT NULL 
          AND mid.inequality_columns IS NOT NULL THEN \',\' ELSE \'\' END 
        + ISNULL (mid.inequality_columns,\'\')
        + \')\' 
        + ISNULL (\' INCLUDE (\' + mid.included_columns + \')\',\'\') AS create_index_statement,migs.*,mid.database_id,mid.[object_id]
    FROM 
        sys.dm_db_missing_index_groups mig
    INNER JOIN 
        sys.dm_db_missing_index_group_stats migs 
    ON migs.group_handle = mig.index_group_handle
    INNER JOIN sys.dm_db_missing_index_details mid 
    ON mig.index_handle = mid.index_handle
    WHERE 
        migs.avg_total_user_cost 
        * (migs.avg_user_impact / 100.0) 
        * (migs.user_seeks + migs.user_scans) > 10
    AND 
        (@DBNAME = db_name(mid.database_id) OR @DBNAME IS NULL)
ORDER BY 
        migs.avg_total_user_cost 
        * migs.avg_user_impact 
        * (migs.user_seeks + migs.user_scans) DESC
END
我对其进行了一些修改以接受数据库名称。如果不提供数据库名称,它将运行并为您提供有关所有数据库的信息,并为您提供有关哪些字段需要索引的建议。 要运行它,请使用:
exec DatabaseName.dbo.SQLMissingIndexes \'MyDatabaseName\'
我通常将可重用的SQL(Sproc)代码放在一个名为
DBA
的单独数据库中,然后从任何数据库中我都可以说: 执行
DBA.dbo.SQLMissingIndexes
举个例子。 编辑 只是想起了消息来源,巴特·邓肯。 这是直接链接http://blogs.msdn.com/b/bartd/archive/2007/07/19/are-you-using-sql-s-missing-index-dmvs.aspx 但是请记住,我确实对其进行了修改,以接受单个数据库名称。     ,从几年前开始,我们就遇到了同样的问题,从SQL Server迁移到OLAP多维数据集,而当最近停止工作时,我们又迁移到Hadoop和其他一些组件。 OLTP(在线事务处理)数据库(其中SQL Server是其中之一)在OLAP(在线分析处理)方面不是很好。这就是OLAP多维数据集的用途。 当您写入和读取许多单独的行时,OLTP提供了良好的吞吐量。正如您刚刚发现的那样,当执行许多需要扫描许多行的聚合查询时,它将失败。由于SQL Server将每个记录存储为磁盘上的连续块,因此扫描许多行意味着需要进行许多磁盘提取。高速缓存可以为您节省一段时间-只要表很小,但是当您访问具有数百万行的表时,问题就变得很明显。 坦白地说,OLAP也不具有可扩展性,并且在某个时候(每天新记录数千万),您将不得不转向分布式的解决方案-付费(Vertica,Greenplum)或免费( HBase,超表)。 如果两者都不是一种选择(例如没有时间或没有预算),那么现在您可以通过在硬件上花费更多来减轻您的痛苦。您需要非常快的IO(快速磁盘,RAID)以及尽可能多的RAM。