Оптимизация запросов для 25+ миллионов строк

11

Я использую MS SQL, и мне нужно выполнить несколько запросов к одной и той же таблице по разным критериям. Сначала я выполнял каждый запрос в исходной таблице, хотя все они имеют некоторую фильтрацию (т. Е. Дата, статус). Это заняло много времени (около 2 минут).

В строках данных есть дубликаты, и все индексы НЕКЛАСТЕРИРОВАНЫ. Меня интересуют только 4 столбца для моих критериев, и результат должен выводить только количество для всех запросов.

колонны необходимо: TABLE, FIELD, AFTER, DATE, и есть индекс по каждому из DATEи TABLE.

После создания временной таблицы только с теми полями, которые мне нужны, она сократилась до 1:40 минут, что все еще очень плохо.

CREATE TABLE #TEMP
(
    TABLE VARCHAR(30) NULL,
    FIELD VARCHAR(30) NULL,
    AFTER VARCHAR(1000) NULL,
    DATE DATETIME,
    SORT_ID INT IDENTITY(1,1)
)
CREATE CLUSTERED INDEX IX_ADT ON #TEMP(SORT_ID)

INSERT INTO #TEMP (TABLE, FIELD, AFTER, DATE)
    SELECT TABLE, FIELD, AFTER, DATE 
    FROM mytbl WITH (NOLOCK)
    WHERE TABLE = 'OTB' AND
    FIELD = 'STATUS'

Runnig this -> (216598 строка (и) затронута)

Поскольку не все запросы зависят от диапазона дат, я не включил его в запрос. Проблема в том, что для вставки требуется более 1 минуты . Вышеуказанная вставка заняла 1:19 мин.

Я хочу запустить что-то вроде этого для нескольких запросов:

SELECT COUNT(*) AS COUNT
FROM #TEMP
WHERE AFTER = 'R' AND
DATE >= '2014-01-01' AND
DATE <= '2015-01-01'

Это проблема с вставкой больше, чем с выделением, но у temp гораздо меньше строк, чем в исходной таблице, что может быть лучше, чем проходить через таблицу несколько раз.

Как я могу оптимизировать это?

РЕДАКТИРОВАТЬ

Я удалил идентификатор сортировки, я думал, что проблема была в основном с выбором, а не вставкой. Это было предположение.

Я не могу создать уникальный для любого индекса, так как нет уникального поля или строк.

Я использую SQL Server 2012.

Таблица Info : Это куча и использует следующее пространство:

name    rows        reserved    data        index_size  unused
mytbl   24869658    9204568 KB  3017952 KB  5816232 KB  370384 KB
Atieh
источник
@MikaelEriksson Я не могу изменить производственные таблицы ..
Atieh
Если запросы, которые вы пытаетесь оптимизировать, имеют форму SELECT COUNT(*) AS COUNT FROM original_table WHERE AFTER = 'R' AND DATE >= '2014-01-01' AND DATE < '2015-01-01', почему бы вам не попробовать оптимизировать каждый (запрос) по отдельности? Вам не разрешено добавлять индексы в таблицу?
ypercubeᵀᴹ
2
Вы должны определить, почему это медленно. Это заблокировано? Ожидание роста tempdb? План исполнения ужасен? Никто не может исправить «мой запрос медленный» без более подробной информации ...
Аарон Бертран
3
Ну, для меня это кажется безнадежным делом ( «Мне не разрешено ничего оптимизировать, поэтому давайте просто помещаем 200К строк во временную таблицу каждый раз, когда нам нужно выполнить некоторые запросы» ). Но вы могли бы удалить TABLEи FIELDстолбцы из #tempтаблицы (все строки имеют TABLE = 'OTB' AND FIELD = 'STATUS'для таблицы конкретного темпа после всех.)
ypercubeᵀᴹ
2
Я попросил внести изменения и улучшения, добавив подробный (и вежливый) комментарий. Для этого и нужны комментарии. Вы также должны пометить свой вопрос версией SQL Server, которую вы используете (например, SQL Server 2014). DDL для таблицы тоже может быть полезным ( CREATE TABLEутверждение). Падение было вызвано тем, что вопрос не был ясным.
Пол Уайт 9

Ответы:

12

Вопрос в основном о том, как оптимизировать оператор select:

SELECT [TABLE], [FIELD], [AFTER], [DATE]
FROM mytbl WITH (NOLOCK)
WHERE [TABLE] = 'OTB' AND
[FIELD] = 'STATUS'

Удаление лишних проекций и добавление предполагаемой dboсхемы:

SELECT [AFTER], [DATE] 
FROM dbo.mytbl WITH (NOLOCK)
WHERE [TABLE] = 'OTB'
AND FIELD = 'STATUS';

Без индекса, как у ([TABLE],[FIELD]) INCLUDE ([AFTER],[DATE])SQL Server, есть два основных варианта:

  1. Сканирование кучи полностью (3 ГБ +); или
  2. Расположить строки , соответствующие [TABLE] = 'OTB'и [FIELD] = 'STATUS'( с помощью IDX6), а затем выполнить кучи (RID) поиск каждой строки для извлечения [AFTER]и [DATE]столбцов.

Если оптимизатор выбирает кучу сканирование или индекс обратиться с РИД поиск зависит от предполагаемой селективности [TABLE] = 'OTB'и [FIELD] = 'STATUS'предикатов. Проверьте, соответствует ли предполагаемое количество строк из поиска действительности. Если нет, обновите свою статистику. Протестируйте запрос с табличной подсказкой, заставляющей использовать индекс, если это условие является достаточно избирательным . Если в настоящее время оптимизатор выбирает поиск по индексу, протестируйте производительность с помощью подсказки INDEX(0)или FORCESCANдля сканирования кучи.

Кроме того, можно немного улучшить сканирование кучи, удалив часть неиспользуемого пространства (370 МБ). В SQL Server 2008 это можно сделать, перестроив кучу. Неиспользуемое пространство в кучах часто является результатом удалений, выполненных без взятия блокировки таблицы (без блокировки таблицы пустые страницы не освобождаются из кучи). По этой причине таблицы с частым удалением часто лучше хранить в виде кластеризованной таблицы.

Производительность сканирования кучи зависит от того, сколько таблицы хранится в памяти, сколько нужно прочитать с диска, насколько заполнены страницы, скорость постоянного хранилища, независимо от того, связано ли сканирование с вводом-выводом или с ЦП ( параллелизм может помочь).

Если производительность по-прежнему неприемлема после того, как вы изучили все вышеперечисленное, попробуйте обосновать новый индекс. Если доступно в вашей версии SQL Server, возможный отфильтрованный индекс для данного запроса будет:

CREATE INDEX index_name
ON dbo.mytbl ([DATE],[AFTER])
WHERE [TABLE] = 'OTB'
AND [FIELD] = 'STATUS';

Также рассмотрите сжатие индекса, если это доступно и выгодно. Без какого-либо нового индекса вы сравнительно мало можете сделать для повышения производительности данного запроса.

Пол Уайт 9
источник
К сожалению Павла, есть: IDX6 nonclustered located on PRIMARY TABLE, FIELD. Может быть, это изменит вещи, которые вы упомянули?
Atieh
6

Я думаю, что есть причина для изменения индексов здесь, потому что:

  • у вас есть задача (эти несколько запросов)
  • объемы хранилищ данных (более 25 миллионов строк) и
  • проблема с производительностью.

Это также было бы хорошим вариантом использования для некластеризованных индексов хранилища столбцов, представленных в SQL Server 2012, т.е. для суммирования / агрегирования нескольких столбцов в большой таблице со многими столбцами.

Хотя у этих индексов есть побочный эффект создания таблицы только для чтения (за исключением переключения разделов), они могут трансформировать производительность агрегированных запросов при соответствующих условиях. Аспект, доступный только для чтения, может управляться путем удаления и повторного создания индекса или простого переключения данных в таблицу.

Я настроил простую испытательную установку, чтобы имитировать ваши настройки, и увидел хорошее улучшение производительности:

USE tempdb
GO

SET NOCOUNT ON
GO

-- Create a large table
IF OBJECT_ID('dbo.largeTable') IS NOT NULL
DROP TABLE dbo.largeTable
GO
CREATE TABLE dbo.largeTable ( 

    [TABLE] VARCHAR(30) NULL,
    FIELD VARCHAR(30) NULL,
    [AFTER] VARCHAR(1000) NULL,
    [DATE] DATETIME,
    SORT_ID INT IDENTITY(1,1),

    pad VARCHAR(100) DEFAULT REPLICATE( '$', 100 )
)
GO

-- Populate table
;WITH cte AS (
SELECT TOP 100000 ROW_NUMBER() OVER ( ORDER BY ( SELECT 1 ) ) rn
FROM master.sys.columns c1
    CROSS JOIN master.sys.columns c2
    CROSS JOIN master.sys.columns c3
)
INSERT INTO dbo.largeTable ( [TABLE], FIELD, [AFTER], [DATE] )
SELECT 
    x.tableName, 
    y.field,
    z.[after],
    DATEADD( day, rn % 1111, '1 Jan 2012' )
FROM cte c
    CROSS JOIN ( VALUES ( 'OTB' ), ( 'AAA' ), ( 'BBB' ), ( 'CCCC' ) ) x ( tableName )
    CROSS JOIN ( VALUES ( 'STATUS' ), ( 'TIME' ), ( 'POWER' ) ) y ( field )
    CROSS JOIN ( VALUES ( 'R' ), ( 'X' ), ( 'Z' ), ( 'A' ) ) z ( [after] )

CHECKPOINT

GO 5

EXEC sp_spaceused 'dbo.largeTable'
GO

SELECT MIN([DATE]) xmin, MAX([DATE]) xmax, FORMAT( COUNT(*), '#,#' ) records
FROM dbo.largeTable
GO

-- Optionally clear cache for more comparable results; DO NOT RUN ON PRODUCTION SYSTEM!!
--DBCC DROPCLEANBUFFERS
--DBCC FREEPROCCACHE
--GO

DECLARE @startDate DATETIME2 = SYSUTCDATETIME()

SELECT COUNT(*) AS COUNT
FROM dbo.largeTable
WHERE [AFTER] = 'R' 
  AND [DATE] >= '2014-01-01' 
  AND [DATE] <= '2015-01-01'

SELECT DATEDIFF( millisecond, @startDate, SYSUTCDATETIME() ) diff1
GO

-- Add the non-clustered columnstore
CREATE NONCLUSTERED COLUMNSTORE INDEX _cs ON dbo.largeTable ( [TABLE], FIELD, [AFTER], [DATE] )
GO

-- Optionally clear cache for more comparable results; DO NOT RUN ON PRODUCTION SYSTEM!!
--DBCC DROPCLEANBUFFERS
--DBCC FREEPROCCACHE
--GO

-- Check query again
DECLARE @startDate DATETIME2 = SYSUTCDATETIME()

SELECT COUNT(*) AS COUNT
FROM dbo.largeTable
WHERE [AFTER] = 'R' 
  AND [DATE] >= '2014-01-01' 
  AND [DATE] <= '2015-01-01'

SELECT DATEDIFF( millisecond, @startDate, SYSUTCDATETIME() ) diff2
GO

Мои результаты, 6 секунд против 0,08 секунд:

введите описание изображения здесь

Таким образом, попробуйте составить кейс с вашим боссом, чтобы изменить индексы, или, по крайней мере, создать какой-то ночной процесс, в котором эти записи будут вырезаны в таблицу / базу данных отчетов только для чтения, где вы сможете выполнять свою работу, и добавить индексирование. подходит для этой рабочей нагрузки.

wBob
источник