Оптимізація запитів для 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'

Виконати це -> (216598 ряд (и) задіяно)

Оскільки не всі запити покладаються на діапазон дат, я не включав його до запиту. Проблема полягає в тому, що для вставки потрібно лише більше 1 хвилини . Наведена вставка зайняла 1:19 хв

Я хочу запустити щось подібне для кількох запитів:

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

Проблема із вставкою більше, ніж з виділенням, але темп має набагато менше рядків, ніж оригінальна таблиця, що може бути краще, ніж кілька разів проходити через таблицю.

Як я можу оптимізувати це?

EDIT

Я видалив ідентифікатор сортування, я вважав, що проблема полягає в основному з виділенням, а не вставкою. Це була здогадка.

Я не можу створити унікальний в жодному індексі, оскільки немає унікального поля чи рядків.

Я використовую SQL Server 2012.

Інформація про таблицю : Це купа і має таке використання місця:

name    rows        reserved    data        index_size  unused
mytbl   24869658    9204568 KB  3017952 KB  5816232 KB  370384 KB

@MikaelEriksson Я не можу змінювати виробничі таблиці ..
Atieh

Якщо запити, які ви намагаєтеся оптимізувати, мають форму SELECT COUNT(*) AS COUNT FROM original_table WHERE AFTER = 'R' AND DATE >= '2014-01-01' AND DATE < '2015-01-01', чому б вам не спробувати оптимізувати кожен (запит) окремо? Вам заборонено додавати індекси до таблиці?
ypercubeᵀᴹ

2
Вам потрібно визначити, чому це повільно. Його заблокують? Чи чекає зростання темпдб? Чи не відповідає план виконання? Ніхто не може виправити "мій запит повільний" без додаткових деталей ...
Аарон Бертран

3
Ну, це здається втраченою причиною для мене ( "мені не дозволяється нічого оптимізувати, тому давайте просто натискати 200K рядків у таблицю темп кожного разу, коли нам потрібно запустити деякі запити" ). Але ви можете видалити стовпці 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]стовпці.

Вибирає оптимізатор пошук скачок або пошук індексів при пошуку RID, залежить від оціночної вибірковості [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';

Також врахуйте стиснення індексу, якщо це доступно і вигідно. Без нового подібного індексу ви можете зробити небагато, щоб покращити ефективність даного запиту.


До жаль Павла, є: IDX6 nonclustered located on PRIMARY TABLE, FIELD. Може, це змінить речі, про які ви згадали?
Атьє

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 секунд v 0,08 секунди:

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

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

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.