WITH TASKS
AS (SELECT session_id,
wait_type,
wait_duration_ms,
blocking_session_id,
resource_description,
PageID = Cast(Right(resource_description, Len(resource_description)-
Charindex(':', resource_description, 3)) As Int)
From sys.dm_os_waiting_tasks
Where wait_type Like 'PAGE%LATCH_%'
And resource_description Like '2:%')
SELECT session_id,
wait_type,
wait_duration_ms,
blocking_session_id,
resource_description,
ResourceType = Case
When PageID = 1 Or PageID % 8088 = 0 Then 'Is PFS Page'
When PageID = 2 Or PageID % 511232 = 0 Then 'Is GAM Page'
When PageID = 3 Or (PageID - 1) % 511232 = 0 Then 'Is SGAM Page'
Else 'Is Not PFS, GAM, or SGAM page'
End
From Tasks ;
解决或避免分配页竞争的问题
一旦你确定tempdb正在遭受分配页竞争,你就可以用下面几种不同的方法降低发生的可能性:
Multiple Tempdb Data Files------如果你是DBA,你或许首先倾向于这个解决方案。每个数据文的开头都有一个分配页的集合,所以,如果你有不只一个文件,那么就能平衡负载。不管怎样,对于你的实例而言,有多个tempdb文件是一个不错的最佳实践,因为很容易做到这点,无风险地降低了竞争发生的可能性。有多个数据文件时,tempdb通过使用成比例填充算法(proportional fill algorithm)来平衡贯穿所有文件的可用空间量。这种做法的效果是选择有最多可用空间的文件,直到该文件和所有其他文件的空间相当。
Temporary Object Reuse------如果你是开发者,你首先要做的可能是优化代码,而不是回顾服务器最佳实践。从SQL Server 2005开始,就可以缓存临时对象的定义以便在需要再次创建的时候复用。再具体一点,就是缓存IAM页(Index Allocation Map)和 一个范围。被复用的对象不需要分配新空间,因此不会有助于任何分配问题。优化代码,确保临时表被缓存,将有助于降低任何潜在的问题。SQL Server默认缓存临时表,所以你首先要检查SQL Server是否缓存了你的临时表。要做到这点,你可以循环跑你的代码,并监视性能监视器计数器“temp table creation rate”在循环开始和结束时的不同。你可以通过如下的脚本来监视:
SET NOCOUNT ON ;
GO
DECLARE @table_counter_before_test BIGINT ;
SELECT @table_counter_before_test = cntr_value
FROM sys.dm_os_performance_counters
WHERE counter_name = 'Temp Tables Creation Rate' ;
DECLARE @i INT = 0 ;
WHILE ( @i < 10 )
BEGIN
EXEC tempdbdemo.dbo.usp_loop_temp_table ;
SELECT @i += 1 ;
END ;
DECLARE @table_counter_after_test BIGINT ;
SELECT @table_counter_after_test = cntr_value
FROM sys.dm_os_performance_counters
WHERE counter_name = 'Temp Tables Creation Rate' ;
PRINT 'Temp tables created during the test: '
+ CONVERT(VARCHAR(100), @table_counter_after_test
- @table_counter_before_test) ;
只要下面某项成立,临时对象就会被缓存:
*指定的约束没有创建;
*DDL语句影响了表,例如CREATE INDEX或CREATE STATISTICS,在表被创建之后没有运行
*对象不是使用动态SQL创建的,例如使用sp_executesql
*对象是在另一个对象里面创建的,例如存储过程、触发器、UDF、用户自定义标志函数返回的表
为了不破坏缓存的规则,我们可以另辟蹊径,例如下面的code:
CREATE PROCEDURE [dbo].[usp_temp_table]
AS
CREATE TABLE #tmpTable
(
c1 INT UNIQUE CLUSTERED, --可以缓存
c2 INT,
c3 CHAR(5000)
) ;
--CREATE UNIQUE CLUSTERED INDEX cix_c1 ON #tmptable ( c1 ) ; --破坏了规则,不能缓存
DECLARE @i INT = 0 ;
WHILE ( @i < 10 )
BEGIN
INSERT INTO #tmpTable ( c1, c2, c3 )
VALUES ( @i, @i + 100, 'coeo' ) ;
SET @i += 1 ;
END ;
GO
跟踪标记 1118------SQL Server 2000已引入,用于帮助减轻SGAM页(2:1:3)上面的竞争,通过在所有数据库中禁用混合范围分配。启用这个跟踪标记后,你创建的每个对象将被分配它自己的范围。SQL Server 2008对混合范围中分配空间引入了改进的算法,因此,你不大可能再SQL Server 2012中遇到这个问题。
SELECT SUM(total_page_count)*8/1024 AS 'tempdb size (MB)',
SUM(total_page_count) AS 'tempdb pages',
SUM(allocated_extent_page_count) AS 'in use pages',
SUM(user_object_reserved_page_count) AS 'user object pages',
SUM(internal_object_reserved_page_count) AS 'internal object pages',
SUM(mixed_extent_page_count) AS 'Total Mixed Extent Pages'
FROM sys.dm_db_file_space_usage ;