代码之家  ›  专栏  ›  技术社区  ›  Abe Miessler

启动或预热SQL Server中的缓存

  •  9
  • Abe Miessler  · 技术社区  · 15 年前

    当我测试一个查询时,我通常会将以下几行放在我测试的内容前面,以确保每次运行查询时都从相同的基线开始。

    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分钟左右。虽然它确实提高了性能,但看起来有点黑客,我很好奇SQL Server中是否有内置的东西可以实现这一点。

    • 这样做是为了改善 您的查询性能闻所未闻 的?
    • 我甚至有权假设 我看到的时间进步 是“已启动”缓存的结果吗?

    如果我对缓存的理解有点模糊,请随意分享您认为可能有用的任何链接或信息。

    更新: 好吧,我很难说我今天试图重现这种行为,但却不能。我在我的工作中与一些人交谈过,他们昨天在数据库上做的一些事情看起来好像是我在proc改进性能之前选择的,而实际上不是。如果有人知道是否可以通过“启动”缓存,我仍然感兴趣。

    3 回复  |  直到 8 年前
        1
  •  3
  •   AdaTheDev    15 年前

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

    我遇见 this 有关如何查看SQL Server缓存中的内容的msdn文章。 这里有一个查询,它将向您显示对象缓存了多少数据页-我已经对其进行了调整,以包括索引名,如下所示:

    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 top语句,请执行存储过程,然后查看运行时缓存中的结束量-可能比较这些结果将指示select top 1与存储过程调用相比所执行的缓存的相对量-并且相对量可能指示性能改进。

    这是非常“大声思考”的东西。我不认为前1名会为存储过程调用显著地启动缓存,但这就是我对这个问题感兴趣的原因!

    我最初会认为这与其他因素(如服务器/磁盘负载)有关。您可以在两种方案之间交替进行3或4次迭代,一次接一次地重复检查Select Top方法实际上是否始终更好(有助于最大限度地降低它成为一次性亮点的风险)

    希望这有助于/使球滚动。

    更新:
    现在你知道,启动缓存的不是Select Top,正如Adrianbanks所说,启动缓存的一个好方法是。至少现在您可以解释什么是意外/混淆的性能差异!将上述脚本保存在库中,这对于检查缓存的状态很有用。

        2
  •  2
  •   adrianbanks    15 年前

    你对你问题的最新情况与我所期望的相符。我看不出 SELECT 1... 查询在随后的查询中可能具有任何实际的性能优势。

    据我所知,SQL Server在运行查询时根据需要将数据页(包含表数据或索引数据)加载到内存中。除非它们被明确清除(使用 DBCC DROPCLEANBUFFERS -即,删除内存中自加载后未更改的任何缓冲区(缓存页),或内存压力(计算机上的可用内存不足或SQL Server上设置的最大内存)。由于这种行为,预热SQL Server数据库以供使用可能是有益的。当您随后运行查询时,收集查询结果所需的数据可能已经在内存中。如果是的话,查询将执行得更快,因为它将产生更少的IO。

    然而,问题在于知道要预缓存什么以及要运行什么查询。您可以对典型的活动运行SQL跟踪,然后将其重放到经常使用的预缓存数据中。但是,如果不让SQL Server保留大量已分配的内存,您总是需要从磁盘读取一些内容(除非您有一个小数据库)。因为您永远不会知道缓存的是什么,而不知道缓存的是什么,所以依赖这种行为来实现性能感觉是错误的。

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

        3
  •  0
  •   rmalayter    8 年前

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

    这将导致从磁盘读取表的所有列,但返回一个可以由SQL递增计算的微小结果。如果您尝试使用 COUNT 或者类似的查询来启动缓存,因为可以通过只加载索引页来回答这些查询。

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