【发布时间】:2014-05-22 20:06:08
【问题描述】:
我们有一个客户端站点,在具有 100+ Gb RAM 的服务器上具有 50Gb SQL 2012 数据库。
在使用应用程序时,SQL Server 在将数据库缓存到内存方面做得很好,但是缓存带来的性能提升发生在第二次运行查询时,而不是第一次。
为了尝试在第一次运行查询时最大化缓存命中,我们编写了一个遍历整个数据库中每个表的每个索引的 proc,运行如下:
SELECT * INTO #Cache
FROM ' + @tablename + ' WITH (INDEX (' + @indexname + '))'
试图强制读取尽可能多的数据。 我们将它安排为每 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
【问题讨论】:
-
每 15 分钟一次?如果您的数据库是 50 GB,并且您已经为 SQL Server 提供了 100+ GB 的内存,那么您应该只需要在启动时执行一次。
-
为什么要插入临时表?
-
@MartinSmith 我怀疑编写它的开发人员发现将所有数据选择到 #temp 表中比在 SSMS 中将输出呈现到一堆网格中更快(可能在非常慢网络连接)。
-
sys.dm_db_index_physical_stats和null用于大多数参数和“详细”,因为最后一个将读取所有页面。虽然不确定它是否会将所有内容都保存在缓存中。有一些机制不喜欢这样做,以避免淹没缓冲池。 -
_GetIndexKeys?你忘了定义吗?我们和你面临同样的问题。
标签: sql-server caching memory