什么会导致 SQL Server 2008R2 上的读取速度变慢?

Ref*_*din 1 sql-server database-tuning sql-server-2008-r2

我们的主数据库大小为 15GB。服务器有 16GB RAM。我们使用简单的恢复模型。

最近,我们注意到我们的自定义应用程序查找(只是使用即席 SQL 语句进行基本读取)速度明显减慢。大多数情况下会多几秒钟。

我不是 SQL Server 管理员,并且不知道接下来该去哪里。

我们监控了CPU,它似乎从未达到峰值。与 RAM 相同。我们有大量的索引,但我认为这只是写入速度的问题......

对我下一步可以看什么有什么建议吗?

Mar*_*son 5

造成这种情况的原因可能有很多。几个问题:

  • 你们做索引维护吗?
  • 你导入数据吗?或者都是由用户通过前端应用程序输入的?

选项 1 - 我们可以查看您的等待统计数据,看看这是否有助于确定查找方向。

选项 2 - 我们可以查看运行缓慢的查询的执行计划。

对于选项 1: 通过 Management Studio 运行此命令(它将运行 30 秒),点击“执行”后,在第二个选项卡中运行运行缓慢的查询之一,然后切换回上一个选项卡并将结果复制并粘贴到此处:

    DECLARE @FinishSampleTime   DATETIME

    IF OBJECT_ID('tempdb..#WaitStats') IS NOT NULL 
        DROP TABLE #WaitStats;

    CREATE TABLE #WaitStats
    (
        Pass TINYINT NOT NULL,
        wait_type NVARCHAR(60),
        wait_time_ms BIGINT,
        signal_wait_time_ms BIGINT,
        waiting_tasks_count BIGINT,
        SampleTime DATETIME
    );

    -- Populate #WaitStats with DMV data. In a second, we'll compare these.
    INSERT #WaitStats(Pass, SampleTime, wait_type, wait_time_ms, signal_wait_time_ms, waiting_tasks_count)
    SELECT
        1 AS Pass,
        GETDATE() AS SampleTime,
        os.wait_type,
        SUM(os.wait_time_ms) OVER (PARTITION BY os.wait_type) as sum_wait_time_ms,
        SUM(os.signal_wait_time_ms) OVER (PARTITION BY os.wait_type ) as sum_signal_wait_time_ms,
        SUM(os.waiting_tasks_count) OVER (PARTITION BY os.wait_type) AS sum_waiting_tasks
    FROM sys.dm_os_wait_stats os
    WHERE
        os.wait_type not in (
            'REQUEST_FOR_DEADLOCK_SEARCH',
            'SQLTRACE_INCREMENTAL_FLUSH_SLEEP',
            'SQLTRACE_BUFFER_FLUSH',
            'LAZYWRITER_SLEEP',
            'XE_TIMER_EVENT',
            'XE_DISPATCHER_WAIT',
            'FT_IFTS_SCHEDULER_IDLE_WAIT',
            'LOGMGR_QUEUE',
            'CHECKPOINT_QUEUE',
            'BROKER_TO_FLUSH',
            'BROKER_TASK_STOP',
            'BROKER_EVENTHANDLER',
            'SLEEP_TASK',
            'WAITFOR',
            'DBMIRROR_DBM_MUTEX',
            'DBMIRROR_EVENTS_QUEUE',
            'DBMIRRORING_CMD',
            'DISPATCHER_QUEUE_SEMAPHORE',
            'BROKER_RECEIVE_WAITFOR',
            'CLR_AUTO_EVENT',
            'DIRTY_PAGE_POLL',
            'HADR_FILESTREAM_IOMGR_IOCOMPLETION',
            'ONDEMAND_TASK_QUEUE',
            'FT_IFTSHC_MUTEX',
            'CLR_MANUAL_EVENT',
            'SP_SERVER_DIAGNOSTICS_SLEEP',
            'HADR_CLUSAPI_CALL',
            'HADR_LOGCAPTURE_WAIT',
            'HADR_TIMER_TASK',
            'HADR_WORK_QUEUE'
        )
    ORDER BY sum_wait_time_ms DESC;

    -- Wait for the specified amount of time
    SET @FinishSampleTime = DATEADD(second,30,GETDATE());
    WAITFOR TIME @FinishSampleTime;

    -- Populate #WaitStats with DMV data. In a second, we'll compare these.
    INSERT #WaitStats(Pass, SampleTime, wait_type, wait_time_ms, signal_wait_time_ms, waiting_tasks_count)
    SELECT
        2 AS Pass,
        GETDATE() AS SampleTime,
        os.wait_type,
        SUM(os.wait_time_ms) OVER (PARTITION BY os.wait_type) as sum_wait_time_ms,
        SUM(os.signal_wait_time_ms) OVER (PARTITION BY os.wait_type ) as sum_signal_wait_time_ms,
        SUM(os.waiting_tasks_count) OVER (PARTITION BY os.wait_type) AS sum_waiting_tasks
    FROM sys.dm_os_wait_stats os
    WHERE
        os.wait_type not in (
            'REQUEST_FOR_DEADLOCK_SEARCH',
            'SQLTRACE_INCREMENTAL_FLUSH_SLEEP',
            'SQLTRACE_BUFFER_FLUSH',
            'LAZYWRITER_SLEEP',
            'XE_TIMER_EVENT',
            'XE_DISPATCHER_WAIT',
            'FT_IFTS_SCHEDULER_IDLE_WAIT',
            'LOGMGR_QUEUE',
            'CHECKPOINT_QUEUE',
            'BROKER_TO_FLUSH',
            'BROKER_TASK_STOP',
            'BROKER_EVENTHANDLER',
            'SLEEP_TASK',
            'WAITFOR',
            'DBMIRROR_DBM_MUTEX',
            'DBMIRROR_EVENTS_QUEUE',
            'DBMIRRORING_CMD',
            'DISPATCHER_QUEUE_SEMAPHORE',
            'BROKER_RECEIVE_WAITFOR',
            'CLR_AUTO_EVENT',
            'DIRTY_PAGE_POLL',
            'HADR_FILESTREAM_IOMGR_IOCOMPLETION',
            'ONDEMAND_TASK_QUEUE',
            'FT_IFTSHC_MUTEX',
            'CLR_MANUAL_EVENT',
            'SP_SERVER_DIAGNOSTICS_SLEEP',
            'HADR_CLUSAPI_CALL',
            'HADR_LOGCAPTURE_WAIT',
            'HADR_TIMER_TASK',
            'HADR_WORK_QUEUE'
        )
    ORDER BY sum_wait_time_ms DESC;

    ----------------------------
    -- What happened: #WaitStats
    ----------------------------
    ;with max_batch as (
        select max(SampleTime) as SampleTime
        from #WaitStats
    )
    SELECT
        'WAIT STATS' as Pattern,
        b.SampleTime as [Sample Ended],
        datediff(ss,wd1.SampleTime, wd2.SampleTime) as [Seconds Sample],
        wd1.wait_type,
        c.[Wait Time (Seconds)],
        c.[Signal Wait Time (Seconds)],
        (wd2.waiting_tasks_count - wd1.waiting_tasks_count) AS [Number of Waits]
    FROM  max_batch b
        JOIN #WaitStats wd2 on
            wd2.SampleTime =b.SampleTime
        JOIN #WaitStats wd1 ON 
            wd1.wait_type=wd2.wait_type AND
            wd2.SampleTime > wd1.SampleTime
        CROSS APPLY (SELECT
            cast((wd2.wait_time_ms-wd1.wait_time_ms)/1000. as numeric(10,1)) as [Wait Time (Seconds)],
            cast((wd2.signal_wait_time_ms - wd1.signal_wait_time_ms)/1000. as numeric(10,1)) as [Signal Wait Time (Seconds)]) AS c
    WHERE (wd2.waiting_tasks_count - wd1.waiting_tasks_count) > 0
        and wd2.wait_time_ms-wd1.wait_time_ms > 0
    ORDER BY [Wait Time (Seconds)] DESC;


--  ===========================================================================
--  ********** Begin Clean-Up **********
--  ===========================================================================
    DROP TABLE #WaitStats
Run Code Online (Sandbox Code Playgroud)

正如我所说,这段代码需要 30 秒才能执行。因此,您点击“执行”,然后在第二个选项卡中运行运行缓慢的查询,然后返回第一个选项卡并等待结果。如果不清楚,请告诉我。

对于选项 2: 突然变慢的查询让我认为索引有问题。如果您的索引严重碎片化,则可能会影响执行计划,从而导致您遇到的问题。

假设您使用 SQL Management Studio 来运行查询,请在执行查询之前按“CTRL+M”。这将生成一个图形执行计划。如果您可以发布屏幕截图,它可能会给我们一些工作依据。这是假设您同意 StackExchange 用户看到您的表/索引名称。

根据您对一切的适应程度,您还可以获得 SQL Sentry Plan Explorer 的免费副本,它将允许您上传匿名执行计划,以便其他人可以深入查看它们。获取它和 SSMS 附加组件。当您按“CTRL+M”后执行查询时,您可以右键单击计划以在计划资源管理器中将其打开。