磁盘 I/O 和 PAGEIOLATCH_XX

Tom*_*Tom 7 performance sql-server-2008-r2 wait-types performance-tuning

最近,我们的一台服务器出现了 CPU 问题,在调查此问题的同时,我们也注意到查询运行缓慢,等待PAGEIOLATCH_XX. 特别是,重新索引作业似乎总是具有这种等待类型。

作为回应,我运行了一个收集sys.dm_io_virtual_file_stats,然后将其分解为时间块并计算出每个操作的平均停顿。虽然主要是尖峰,但磁盘似乎有规律地低于 20 毫秒的值。据我所知,20 毫秒是推荐值(?)。

除此之外,我还运行了 Glenn Barry 的脚本:

select db_name(database_id) as DatabaseName, file_id
,io_stall_read_ms
,num_of_reads
,cast(io_stall_read_ms/(1.0+num_of_reads) as numeric(10,1)) as 'avg_read_stall_ms'
,io_stall_write_ms
,num_of_writes
,cast(io_stall_write_ms/(1.0+num_of_writes) as numeric(10,1)) as 'avg_write_stall_ms'
,io_stall_read_ms + io_stall_write_ms as io_stalls
,num_of_reads + num_of_writes as total_io
,cast((io_stall_read_ms+io_stall_write_ms)/(1.0+num_of_reads +
num_of_writes) as numeric(10,1)) as 'avg_io_stall_ms'
from sys.dm_io_virtual_file_stats(null,null) --where db_name(database_id) = 'tempdb'
order by [DatabaseName] desc'
Run Code Online (Sandbox Code Playgroud)

它还计算平均 I/O 停顿,这也确认停顿时间小于 20 毫秒。

我还查看了以下内容,看看是否有任何挂起的任务花费的时间比建议的要长,但这并没有抛出任何挂起的 I/O 操作的时间通常超过 20 毫秒。

SELECT db_name(database_id) as 'Database',
file_name(file_id) as 'File',
io_stall,
io_pending_ms_ticks
FROM sys.dm_io_virtual_file_stats(NULL, NULL) iovfs,
 sys.dm_io_pending_io_requests as iopior
WHERE iovfs.file_handle = iopior.io_handle
Run Code Online (Sandbox Code Playgroud)

我现在的问题是:如果问题与磁盘无关,为什么我会看到很多 PAGEIOLATCH_XX 等待?特别是,为什么这种等待类型的重新索引运行速度非常慢?

这可能与CPU压力有关吗?

================================================== ==============================

我只是想更新线程。在做了更多的分析之后,我找到了一个导致大量读取的特定过程。过程如下:

ALTER PROCEDURE [dbo].[GetActiveSessionCount]
    @SessionCount   INTEGER OUTPUT
AS
SET NOCOUNT ON
BEGIN
    DECLARE @Error              INTEGER,
            @RowCount           INTEGER,
            @nExpireAfter       INTEGER
    SELECT  @nExpireAfter = ExpireSessionsAfter FROM KSYSTEM
    SELECT @Error = @@ERROR, @RowCount = @@ROWCOUNT
IF(1 <> @RowCount)
BEGIN
RAISERROR (50003, 15, 1, 'GetActiveSessionCount')
RETURN 50003
END
    IF (0 <> @Error)
    BEGIN
        RETURN @Error
    END
    SELECT  @SessionCount = COUNT(SessionID)
    FROM    KSESSION  WITH (NOLOCK)
    WHERE
    (
        (
            Expirable = 0
        )
        OR
        (
            Expirable = 1
            AND
            (   --SessionID IS NOT NULL)
                EXISTS (SELECT SessionID FROM KFILESAWAITINGCOMMIT fac WITH (NOLOCK) WHERE SessionID = fac.SessionID)
                OR
                (
                    LastAccessDateTime IS NOT NULL
                    AND GETDATE() <= (DATEADD(minute, @nExpireAfter, LastAccessDateTime))
                )
            )
        )
    )

SELECT @Error = @@ERROR
IF(@Error <> 0)
BEGIN
RETURN @Error
END
    RETURN 0
END
Run Code Online (Sandbox Code Playgroud)

使用STATISTICS IO我可以看到问题行是

SELECT SessionID FROM KFILESAWAITINGCOMMIT fac WITH (NOLOCK) WHERE SessionID = fac.SessionID
Run Code Online (Sandbox Code Playgroud)

查看执行计划,它正在执行聚集索引扫描。现在,该表上有一个非聚集索引,已经专门用于 SessionID,但是它没有被使用。

我在测试中发现的是,如果我自己运行它SELECT,那么它使用非聚集索引并且性能良好。但是如果我在 proc 中使用一个提示来强制它使用非聚集索引,那么它实际上会表现得更糟。

谁能解释一下?

Jam*_*son 4

当 SQL Server 等待从磁盘读取数据时,会记录 PAGEIOLATCH_XX 等待。索引维护是一项众所周知的密集操作,因此应该在最安静的时间执行,以避免对生产产生任何影响。

您提到您的查询导致了相同的等待。如果这与索引维护同时发生,那么这并不奇怪,但如果它发生在其他时间,则可能是由于内存压力(RAM 中没有足够的空间来存储页面,因此需要再次从磁盘读取它们) )、大型扫描,甚至可能表明您的磁盘存在潜在问题。需要更多的调查来排除这些可能性。