在SQL Server中启动或加热缓存

时间:2010-02-03 23:23:22

标签: sql-server sql-server-2005 caching

当我测试查询时,我通常会在我正在测试的任何内容之前删除以下行,以确保每次运行查询时都从相同的基线开始。

CHECKPOINT
GO
DBCC FREEPROCCACHE
GO
DBCC DROPCLEANBUFFERS
GO
EXEC sp_MyProc 12345

在我今天运行的存储过程中,我注意到当我用这些线路运行它时,每次花费大约18分钟。当我离开这些线路时,它只花了3个。看到由于清除缓存与已准备好的缓存造成的巨大差异,我决定添加以下内容,看看我是否可以在运行我的proc之前手动填充缓存,看看它做了什么性能。

CHECKPOINT
GO
DBCC FREEPROCCACHE
GO
DBCC DROPCLEANBUFFERS
GO
SELECT top 1 '1' from Table1
EXEC sp_MyProc 12345

您可能已经猜到sp_MyProc使用Table1了很多。我惊讶地发现,这样做会让我的运行时间持续下降到大约6分钟。虽然它确实提高了性能,但它看起来有点hackish,我很好奇SQL Server内置了什么内容才能实现这一目标。

  • 这样做是为了改善 您的查询的性能闻所未闻 的?
  • 我甚至认为这是正确的 我看到的时间的改善 是“Primed”缓存的结果?

如果我对缓存的理解有点偏差,请随时分享您认为可能有帮助的任何链接或信息。

更新 好吧,我很尴尬地说我今天试图重现这种行为,但却无法做到。我在工作中与一些人交谈,看起来他们昨天在数据库上做的一些事情可能让我看起来好像我在proc之前的选择提高了性能,而事实上并非如此。我仍然有兴趣听听是否有人知道是否可以“启动”缓存。

3 个答案:

答案 0 :(得分:3)

提供“答案”以便尝试解决这个问题,因为这是我特别感兴趣的事情。

我遇到了this MSDN文章,介绍了如何查看SQL Server缓存中的内容。 那里有一个查询,它将显示对象缓存了多少数据页面 - 我调整它只是为了包含索引名称,如下所示:

SELECT count(*) AS cached_pages_count, obj.name, index_id, i.name AS IndexName
FROM sys.dm_os_buffer_descriptors AS bd 
    INNER JOIN 
    (
        SELECT object_id, object_name(object_id) AS name 
            ,index_id ,allocation_unit_id
        FROM sys.allocation_units AS au
            INNER JOIN sys.partitions AS p 
                ON au.container_id = p.hobt_id 
                    AND (au.type = 1 OR au.type = 3)
        UNION ALL
        SELECT object_id, object_name(object_id) AS name   
            ,index_id, allocation_unit_id
        FROM sys.allocation_units AS au
            INNER JOIN sys.partitions AS p 
                ON au.container_id = p.partition_id 
                    AND au.type = 2
    ) AS obj 
        ON bd.allocation_unit_id = obj.allocation_unit_id
    LEFT JOIN sysindexes i ON obj.object_id = i.id AND obj.index_id = i.indid
WHERE database_id = db_id()
GROUP BY obj.name, index_id, i.name
ORDER BY cached_pages_count DESC;

如果您尝试以下步骤,您应该能够看到有关缓存的内容。在您的数据库中执行这些操作(而不是例如master):

1)检查点+清除缓存下来
2)运行上面的查询,你应该得到返回1条记录(对于sysobjvalues),但对于表1没有任何内容 3)现在运行SELECT TOP 1 '1' FROM MyTable语句
4)重新运行上述查询并查看结果中现在显示的内容 - 您可能会看到MyTable显示缓存页面的记录 - 记下该数字

这应该可以指示初始SELECT发生的数据缓存级别。如果你再次重复这个过程,而不是SELECT TOP语句,执行你的sproc,然后看看在运行时缓存中有多少结果 - 也许比较这些结果将表明缓存的相对数量是由与sproc调用相比,SELECT TOP 1 - 相对数量可以表明性能提升。

这非常“大声思考”的东西。我不会想到TOP 1会为sproc调用显着地启动缓存,但这就是我对这个问题感兴趣的原因!

我原本以为它更多地与其他因素(例如服务器/磁盘负载)有关。您可以在2个场景之间交替进行3次或4次迭代,一个接一个地进行切换,以仔细检查SELECT TOP方法是否实际上更好(有助于最小化它是一次性blip的风险)

希望这有助于推动球的发展。

<强>更新
现在你知道它不是启动缓存的SELECT TOP,这是一个很好的方法来填充缓存正如AdrianBanks所说。至少现在你可以解释什么是意外/混淆性能差异!将上述脚本保留在库中,这对于检查缓存的状态非常有用。

答案 1 :(得分:2)

您对问题的更新符合我的预期。我无法看到运行SELECT 1...查询如何在后续查询中获得任何真正的性能优势。

据我了解,SQL Server在运行查询时需要将数据页(包含表数据或索引数据)加载到内存中。这些都保存在内存中,除非它们被明确清除(使用DBCC DROPCLEANBUFFERS - 即删除内存中任何缓冲区(缓存页面),这些缓冲区在加载后没有被更改),或者存在内存压力(内存压力很低)机器或SQL Server上设置的最大内存)。由于这种行为,预热SQL Server数据库以供使用可能是有益的。当您随后运行查询时,收集查询结果所需的数据可能已经在内存中。如果是,则查询将执行得更快,因为它将导致更少的IO。

然而,问题在于知道预缓存的内容以及运行的查询。您可以对典型活动运行SQL跟踪,然后重播它以预先缓存经常使用的数据。虽然不让SQL Server保留大量已分配的内存,但您总是必须从磁盘读取一些内容(除非您有一个小型数据库)。因为你永远不会知道什么是缓存,什么不是,所以依靠这种行为来表现是错误的。

我会集中精力通过阅读更少的数据或尽可能使用索引来提高查询效率。这也将为您提供一般的好处以及冷启动时的更好性能。

答案 2 :(得分:0)

为全表数据(或其子集)填充SQL Server缓存的一种方法是运行: SELECT SUM(CAST(BINARY_CHECKSUM(*) AS BIGINT)) FROM my_table

这会导致从磁盘读取表的所有列,但返回一个可以由SQL逐步计算的微小结果。如果您尝试使用COUNT或类似的查询来填充缓存,SQL优化器将不会这样做,因为可以通过仅加载索引页来回答这些问题。

根据需要调整列并将WHERE子句语句添加到缓存索引或表子集。