31.8.26

Проблемы производительности из-за ORDER BY/GROUP BY — сбросы в tempdb

Автор: Sarjen Haque, Performance issues from ORDER BY/GROUP BY - spills in tempdb

Совершенно обычно и ожидаемо видеть запрос, содержащий предложение ORDER BY или GROUP BY для целей отображения или группировки. Также часто разработчики используют предложение ORDER BY по привычке, не задумываясь о его необходимости. В результате запросы со временем замедляются по мере увеличения количества записей.

Когда операция сортировки не может получить достаточный грант памяти и не может быть выполнена в памяти, она должна использовать промежуточную материализацию tempdb. Более высокая нагрузка на tempdb значительно ухудшает общую производительность SQL Server. Эта ситуация обычно известна как «spill to tempdb» или «spills in tempdb». Крайне важно выявлять такие предупреждения о сортировке и по возможности избегать их.

По моему опыту, я видел, как ORDER BY/GROUP BY используется для столбца VARCHAR (8000) при извлечении данных; и даже неразумно используется в предложении JOIN! Настройка таких запросов немного сложна, и в большинстве случаев это невозможно, поскольку внешнее приложение или бизнес-логика уже построены на этом критерии. Создание индекса на этом столбце невозможно из-за ограничения в 900 байт для столбца ключа индекса. Таким образом, кроме как скрестить пальцы, мало что можно сделать для немедленного решения проблемы с производительностью.

Общие проблемы

Ниже приведены некоторые распространённые проблемы, возникающие из-за неправильного использования предложений ORDER BY/GROUP BY:

  1. Быстрый рост файлов данных tempdb.
  2. Увеличение активности дискового ввода-вывода на tempdb и диске tempdb.
  3. Появление конкуренции за блокировки и эскалации блокировок.
  4. Увеличение гранта памяти для операции сортировки/хэширования.
  5. Появление параллельного плана запроса.

Обнаружение проблемы

Обнаружение проблем с производительностью, возникающих из-за операции сортировки, довольно простое и понятное. Ниже приведены некоторые советы по выявлению проблем:

  1. Просмотрите запрос и определите столбцы, используемые в предложениях ORDER/GROUP.
  2. Просмотрите план выполнения и найдите операторы «sort».
  3. Определите операторы параллелизма, которые выполняют «распределение потоков», «сбор потоков» и «перераспределение потоков» в параллельном плане выполнения.
  4. Используйте событие трассировки SQL Profiler «sort warnings».
  5. Расширенное событие (Extended Event) — «sort_warning».
  6. Используйте PerfMon или sys.dm_os_performance для отслеживания «worktables created/sec» и «workfiles created/sec».

Решение проблемы с производительностью

Для решения проблем с производительностью, возникающих из-за операции сортировки, можно предпринять несколько действий:

  1. Проверьте необходимость операции сортировки в запросе.
  2. Попробуйте выполнять сортировку на стороне клиентского приложения.
  3. Нормализуйте схему базы данных.
  4. Создайте один или несколько индексов.
  5. Примените фильтры к индексам.
  6. Используйте TOP (n), когда есть «ORDER BY», если это возможно.
  7. Добавьте больше фильтров в запрос, чтобы обрабатывать меньше данных.
  8. Обновите статистику распределения.

Наблюдение за поведением

Чтобы наблюдать за типичными проблемами с операциями ORDER BY/GROUP BY, давайте создадим базу данных, таблицу и выполним простой запрос SELECT на 500 000 записей.

CREATE DATABASE testDB
GO
USE testDB
GO

SET NOCOUNT ON

IF OBJECT_ID('tblLarge') IS NOT NULL
    DROP TABLE tblLarge
GO

CREATE TABLE tblLarge
    (
      xID INT IDENTITY(1, 1) ,
      sName1 VARCHAR(100) ,
      sName2 VARCHAR(1000) ,
      sName3 VARCHAR(400) ,
      sIdentifier CHAR(100) ,
      dDOB DATETIME NULL ,
      nWage NUMERIC(20, 2) ,
      sLicense VARCHAR(25)
    )
GO

/*********************************
Add 500000 records
**********************************/

SET NOCOUNT ON
INSERT  INTO tblLarge
        ( sName1 ,
          sName2 ,
          sName3 ,
          sIdentifier ,
          dDOB ,
          nWage ,
          sLicense
        )
VALUES  ( LEFT(CAST(NEWID() AS VARCHAR(36)), RAND() * 50) ,    -- sName1
          LEFT(CAST(NEWID() AS VARCHAR(36)), RAND() * 60) ,    -- sName2
          LEFT(CAST(NEWID() AS VARCHAR(36)), RAND() * 70) ,    -- sName2
          LEFT(CAST(NEWID() AS VARCHAR(36)), 2) ,              -- sIdentifier    
          DATEADD(dd, -RAND() * 20000, GETDATE()) ,            -- dDOB
          ( RAND() * 1000 ) ,                                  -- nWage
          SUBSTRING(CAST(NEWID() AS VARCHAR(36)), 6, 7)        -- sLicense       
        )
GO 500000

/******************************************************
** Create a clustered index
******************************************************/
ALTER TABLE [tblLarge]
       ADD  CONSTRAINT [PK_tblLarge]
       PRIMARY KEY CLUSTERED ([xID] ASC)

/***************************************************************
** To resolve the sort warning, create a non-clustered index
***************************************************************/
CREATE NONCLUSTERED INDEX [IX_sName1]
       ON [tblLarge] ([sName1] ASC)

Простые операторы SELECT

Ниже приведены несколько простых операторов SELECT для воспроизведения поведения.

/******************************************************
** Simple select statement
******************************************************/
--First query
SELECT  xID ,
        sName1
FROM    tblLarge

-- Second query with - ORDER BY
SELECT  xID ,
        sName1
FROM    tblLarge
ORDER BY sName1

-- Third query - GROUP BY/ORDER BY
SELECT  sName1 ,
        COUNT(sName1) AS nCount
FROM    tblLarge a
GROUP BY sName1
ORDER BY sName1

Используя расширенные события (SQL 2012), трассировку SQL Profiler и план выполнения, «sort warning» легко обнаруживаются. Ниже приведены некоторые выходные данные.

Рисунок №1: Предупреждение о сортировке с использованием расширенных событий в SQL 2012

Рисунок №2A: Обнаружение предупреждения о сортировке с использованием плана выполнения

Рисунок №2B: Обнаружение предупреждения о сортировке с использованием плана выполнения

Рисунок №3: Обнаружение предупреждения о сортировке с использованием трассировки SQL Profiler

Подробнее






Комментариев нет:

Отправить комментарий