强制SQL Server将整个数据库预先缓存到内存中

El *_*ark 26 memory sql-server caching

我们在具有100+ Gb RAM的服务器上有一个带有50Gb SQL 2012数据库的客户端站点.

在使用应用程序时,SQL服务器可以很好地将数据库缓存到内存中,但是缓存的性能提高是在第二次运行查询时发生的,而不是第一次.

为了在第一次运行查询时尝试最大化缓存命中,我们编写了一个proc,它遍历整个数据库中每个表的每个索引,运行以下命令:

SELECT * INTO #Cache 
FROM ' + @tablename + ' WITH (INDEX (' + @indexname + '))'
Run Code Online (Sandbox Code Playgroud)

为了尽可能多的数据,试图强迫一个大的,丑陋的,人为的阅读.我们计划每15分钟运行一次,它总体上做得很好.

在没有讨论其他瓶颈,硬件规格,查询计划或查询优化的情况下,是否有人对如何完成同样的任务有更好的想法?

更新
感谢您的建议.删除了"INTO #Cache".经过测试,它对填充缓冲区没有任何影响.
补充:我只选择索引中的键,而不是选择*.这(显然)更为重要,速度更快.
补充:读取和缓存约束索引.

这是当前的代码:(希望它对其他人有用)

CREATE VIEW _IndexView
as
-- Easy way to access sysobject and sysindex data
SELECT 
so.name as tablename,
si.name as indexname,
CASE si.indid WHEN 1 THEN 1 ELSE 0 END as isClustered,
CASE WHEN (si.status & 2)<>0 then 1 else 0 end as isUnique,
dbo._GetIndexKeys(so.name, si.indid) as Keys,
    CONVERT(bit,CASE WHEN EXISTS (SELECT * FROM sysconstraints sc WHERE object_name(sc.constid) = si.name) THEN 1 ELSE 0 END) as IsConstraintIndex
FROM    sysobjects so
INNER JOIN sysindexes si ON so.id = si.id
WHERE   (so.xtype = 'U')--User Table
AND     ((si.status & 64) = 0) --Not statistics index
AND (   (si.indid = 0) AND (so.name <> si.name) --not a default clustered index
        OR
        (si.indid > 0)
    )
AND si.indid <> 255 --is not a system index placeholder

UNION
SELECT 
so.name as tablename,
si.name as indexname,
CASE si.indid WHEN 1 THEN 1 ELSE 0 END as isClustered,
CASE WHEN (si.status & 2)<>0 then 1 else 0 end as isUnique,
dbo._GetIndexKeys(so.name, si.indid) as Keys,
CONVERT(bit,0) as IsConstraintIndex
FROM    sysobjects so
INNER JOIN sysindexes si ON so.id = si.id
WHERE   (so.xtype = 'V')--View
AND     ((si.status & 64) = 0) --Not statistics index
GO


CREATE PROCEDURE _CacheTableToSQLMemory
@tablename varchar(100)
AS
BEGIN
DECLARE @indexname varchar(100)
DECLARE @xtype varchar(10)
DECLARE @SQL varchar(MAX)
DECLARE @keys varchar(1000)

DECLARE @cur CURSOR
SET @cur = CURSOR FOR
SELECT  v.IndexName, so.xtype, v.keys
FROM    _IndexView v
INNER JOIN sysobjects so ON so.name = v.tablename
WHERE   tablename = @tablename

PRINT 'Caching Table ' + @Tablename
OPEN @cur
FETCH NEXT FROM @cur INTO @indexname, @xtype, @keys
WHILE (@@FETCH_STATUS = 0)
BEGIN
        PRINT '    Index ' + @indexname
        --BEGIN TRAN
            IF @xtype = 'V'
                SET @SQL = 'SELECT ' + @keys + ' FROM ' + @tablename + ' WITH (noexpand, INDEX (' + @indexname + '))' --
            ELSE
                SET @SQL = 'SELECT ' + @keys + ' FROM ' + @tablename + ' WITH (INDEX (' + @indexname + '))' --

            EXEC(@SQL)
        --ROLLBACK TRAN
        FETCH NEXT FROM @cur INTO @indexname, @xtype, @keys
END
CLOSE @cur
DEALLOCATE @cur

END
GO
Run Code Online (Sandbox Code Playgroud)

Joe*_*orn 19

首先,有一个名为"Minumum Server Memory"的设置看起来很诱人.忽略它.来自MSDN:

数据库引擎获取的内存量完全取决于实例上的工作负载.未处理许多请求的SQL Server实例可能永远不会到达最小服务器内存.

这告诉我们设置更大的最小内存不会强制或鼓励任何预缓存.您可能有其他原因设置此项,但预先填充缓冲池不是其中之一.

那么你可以做些什么来预加载数据呢?这很简单.只需设置代理作业即可select *从每个表中执行操作.您可以将其安排为"在Sql Agent启动时自动启动".换句话说,你已经在做的事情非常接近于处理这个问题的标准方法.

但是,我确实需要建议三个变化:

  1. 不要尝试使用临时表.只需从表中选择即可.您无需对结果执行任何操作即可使Sql Server加载缓冲池:您需要做的就是选择.一个临时表可以强制sql server在加载后从缓冲池中复制数据...你最终(简要地)存储了两次.
  2. 不要每15分钟运行一次.只需在启动时运行一次,然后不管它.一旦分配,Sql Server释放内存需要很多.只是不需要一遍又一遍地重新运行它.
  3. 不要试图暗示索引.提示只是:提示.Sql Server可以自由地忽略这些提示,并且它将对没有明确用于索引的查询执行此操作.确保索引预先加载的最佳方法是构造一个显然使用该索引的查询.这里的一个具体建议是以与索引相同的顺序对结果进行排序.这通常会帮助Sql Server使用该索引,因为它可以"遍历索引"以产生结果.