Как преднамеренно фрагментировать индекс SQL Server?


9

Я хотел бы намеренно создавать условия плохих индексов для имеющейся у меня тестовой базы данных SQL Server 2017, просто чтобы лучше понять эти сценарии обслуживания? Индекс SQL Server и ведение статистики

Существует ли быстрый / автоматический способ поставить под угрозу целостность индекса или увеличить его фрагментацию? Знаете ли вы какой-нибудь полезный ресурс, на который я могу посмотреть, чтобы добиться этого?


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

3
Вы хотите для одного индекса или всех индексов в базе данных? Если вы хотите для всех индексов, то сожмите свою базу данных -DBCC SHRINKDATABASE ([yourNONProdDB])
Кин Шах

Все индексы БД были бы идеальными. Спасибо @KinShah
Мороро

Ответы:


10

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

CREATE TABLE dbo.Tests (Id UNIQUEIDENTIFIER PRIMARY KEY);
GO
INSERT INTO dbo.Tests (Id)
WITH x AS (SELECT n FROM (VALUES (0),(1),(2),(3),(4),(5),(6),(7),(8),(9)) v(n))
SELECT NEWID()
FROM x AS x1, x AS x2, x AS x3, x AS x4, x AS x5, x AS x6;

Это сгенерирует миллион строк.

Зная, что NEWID()это не гарантирует какого-либо порядка, SQL Server придется вставлять в случайные места в таблице - это собирается фрагментировать первичный ключ.


1
Это не будет работать или, по крайней мере, не гарантировано: SQL Server может и, вероятно, будет сортировать строки (спулинг на диск при необходимости из-за количества строк) перед добавлением их в индекс. Чтобы вызвать фрагментацию, вам нужно выполнить много отдельных вставок - в SSMS: INSERT dbo.Tests (Id) SELECT NEWID(); GO 1000000;- это, очевидно, займет больше времени. См. Pastebin.com/SVLtiRnP для примера, который я собрал вместе для другого вопроса. Использование строк переменной длины и их случайное обновление может более эффективно привести к фрагментации?
Дэвид

3

Я хотел сделать несколько «Гадких» индексов, поэтому я сделал следующее. Это работало хорошо

-- Create databases to test index job, each database is about 800MB with 100,000 GUID primary keys, in each of two tables
-- Create 6 database to test index job for DatabasesInParallel Database design based on example https://dba.stackexchange.com/q/9821/21924

--Drop last test
USE [master]
exec asp_kill_user_connections [IndexTest_1]
exec asp_kill_user_connections [IndexTest_2]
exec asp_kill_user_connections [IndexTest_3]
exec asp_kill_user_connections [IndexTest_4]
exec asp_kill_user_connections [IndexTest_5]
exec asp_kill_user_connections [IndexTest_6]
GO

DROP DATABASE [IndexTest_1]
GO
DROP DATABASE [IndexTest_2]
GO
DROP DATABASE [IndexTest_3]
GO
DROP DATABASE [IndexTest_4]
GO
DROP DATABASE [IndexTest_5]
GO
DROP DATABASE [IndexTest_6]
GO

-- create [IndexTest_1]
USE [master];
GO

CREATE DATABASE [IndexTest_1];
GO

USE IndexTest_1

SET NOCOUNT ON

CREATE TABLE TestGuidA (Id UNIQUEIDENTIFIER NOT NULL DEFAULT NEWID() PRIMARY KEY,
SomeDate DATETIME, batchNumber BIGINT, FILLER CHAR(100))

CREATE TABLE TestGuidB (Id UNIQUEIDENTIFIER NOT NULL DEFAULT NEWID() PRIMARY KEY,
SomeDate DATETIME, batchNumber BIGINT, FILLER CHAR(100))

DECLARE @BatchCounter INT = 1
DECLARE @Numrows INT = 100000


WHILE (@BatchCounter <= 20)
BEGIN 
BEGIN TRAN

DECLARE @LocalCounter INT = 0

    WHILE (@LocalCounter <= @NumRows)
    BEGIN
    INSERT TestGuidA (SomeDate,batchNumber) VALUES (GETDATE(),@BatchCounter)
    SET @LocalCounter +=1
    END

SET @LocalCounter = 0

    WHILE (@LocalCounter <= @NumRows)
    BEGIN
    INSERT TestGuidB (SomeDate,batchNumber) VALUES (GETDATE(),@BatchCounter)
    SET @LocalCounter +=1
    END

SET @BatchCounter +=1
COMMIT 
END

-----------------------------------------------

-- create [IndexTest_2]
USE [master];
GO

CREATE DATABASE [IndexTest_2];
GO

USE IndexTest_2

SET NOCOUNT ON

CREATE TABLE TestGuidA (Id UNIQUEIDENTIFIER NOT NULL DEFAULT NEWID() PRIMARY KEY,
SomeDate DATETIME, batchNumber BIGINT, FILLER CHAR(100))

CREATE TABLE TestGuidB (Id UNIQUEIDENTIFIER NOT NULL DEFAULT NEWID() PRIMARY KEY,
SomeDate DATETIME, batchNumber BIGINT, FILLER CHAR(100))

DECLARE @BatchCounter INT = 1
DECLARE @Numrows INT = 100000


WHILE (@BatchCounter <= 20)
BEGIN 
BEGIN TRAN

DECLARE @LocalCounter INT = 0

    WHILE (@LocalCounter <= @NumRows)
    BEGIN
    INSERT TestGuidA (SomeDate,batchNumber) VALUES (GETDATE(),@BatchCounter)
    SET @LocalCounter +=1
    END

SET @LocalCounter = 0

    WHILE (@LocalCounter <= @NumRows)
    BEGIN
    INSERT TestGuidB (SomeDate,batchNumber) VALUES (GETDATE(),@BatchCounter)
    SET @LocalCounter +=1
    END

SET @BatchCounter +=1
COMMIT 
END

------------------------------------

-- create [IndexTest_3]
USE [master];
GO

CREATE DATABASE [IndexTest_3];
GO

USE IndexTest_3

SET NOCOUNT ON

CREATE TABLE TestGuidA (Id UNIQUEIDENTIFIER NOT NULL DEFAULT NEWID() PRIMARY KEY,
SomeDate DATETIME, batchNumber BIGINT, FILLER CHAR(100))

CREATE TABLE TestGuidB (Id UNIQUEIDENTIFIER NOT NULL DEFAULT NEWID() PRIMARY KEY,
SomeDate DATETIME, batchNumber BIGINT, FILLER CHAR(100))

DECLARE @BatchCounter INT = 1
DECLARE @Numrows INT = 100000


WHILE (@BatchCounter <= 20)
BEGIN 
BEGIN TRAN

DECLARE @LocalCounter INT = 0

    WHILE (@LocalCounter <= @NumRows)
    BEGIN
    INSERT TestGuidA (SomeDate,batchNumber) VALUES (GETDATE(),@BatchCounter)
    SET @LocalCounter +=1
    END

SET @LocalCounter = 0

    WHILE (@LocalCounter <= @NumRows)
    BEGIN
    INSERT TestGuidB (SomeDate,batchNumber) VALUES (GETDATE(),@BatchCounter)
    SET @LocalCounter +=1
    END

SET @BatchCounter +=1
COMMIT 
END

----------------------------------------
-- create [IndexTest_4]
USE [master];
GO

CREATE DATABASE [IndexTest_4];
GO

USE IndexTest_4

SET NOCOUNT ON

CREATE TABLE TestGuidA (Id UNIQUEIDENTIFIER NOT NULL DEFAULT NEWID() PRIMARY KEY,
SomeDate DATETIME, batchNumber BIGINT, FILLER CHAR(100))

CREATE TABLE TestGuidB (Id UNIQUEIDENTIFIER NOT NULL DEFAULT NEWID() PRIMARY KEY,
SomeDate DATETIME, batchNumber BIGINT, FILLER CHAR(100))

DECLARE @BatchCounter INT = 1
DECLARE @Numrows INT = 100000


WHILE (@BatchCounter <= 20)
BEGIN 
BEGIN TRAN

DECLARE @LocalCounter INT = 0

    WHILE (@LocalCounter <= @NumRows)
    BEGIN
    INSERT TestGuidA (SomeDate,batchNumber) VALUES (GETDATE(),@BatchCounter)
    SET @LocalCounter +=1
    END

SET @LocalCounter = 0

    WHILE (@LocalCounter <= @NumRows)
    BEGIN
    INSERT TestGuidB (SomeDate,batchNumber) VALUES (GETDATE(),@BatchCounter)
    SET @LocalCounter +=1
    END

SET @BatchCounter +=1
COMMIT 
END

------------------------------------------------
-- create [IndexTest_5]
USE [master];
GO

CREATE DATABASE [IndexTest_5];
GO

USE IndexTest_5

SET NOCOUNT ON

CREATE TABLE TestGuidA (Id UNIQUEIDENTIFIER NOT NULL DEFAULT NEWID() PRIMARY KEY,
SomeDate DATETIME, batchNumber BIGINT, FILLER CHAR(100))

CREATE TABLE TestGuidB (Id UNIQUEIDENTIFIER NOT NULL DEFAULT NEWID() PRIMARY KEY,
SomeDate DATETIME, batchNumber BIGINT, FILLER CHAR(100))

DECLARE @BatchCounter INT = 1
DECLARE @Numrows INT = 100000


WHILE (@BatchCounter <= 20)
BEGIN 
BEGIN TRAN

DECLARE @LocalCounter INT = 0

    WHILE (@LocalCounter <= @NumRows)
    BEGIN
    INSERT TestGuidA (SomeDate,batchNumber) VALUES (GETDATE(),@BatchCounter)
    SET @LocalCounter +=1
    END

SET @LocalCounter = 0

    WHILE (@LocalCounter <= @NumRows)
    BEGIN
    INSERT TestGuidB (SomeDate,batchNumber) VALUES (GETDATE(),@BatchCounter)
    SET @LocalCounter +=1
    END

SET @BatchCounter +=1
COMMIT 
END
--------------------------------------------

-- create [IndexTest_6]
USE [master];
GO

CREATE DATABASE [IndexTest_6];
GO

USE IndexTest_6

SET NOCOUNT ON

CREATE TABLE TestGuidA (Id UNIQUEIDENTIFIER NOT NULL DEFAULT NEWID() PRIMARY KEY,
SomeDate DATETIME, batchNumber BIGINT, FILLER CHAR(100))

CREATE TABLE TestGuidB (Id UNIQUEIDENTIFIER NOT NULL DEFAULT NEWID() PRIMARY KEY,
SomeDate DATETIME, batchNumber BIGINT, FILLER CHAR(100))

DECLARE @BatchCounter INT = 1
DECLARE @Numrows INT = 100000


WHILE (@BatchCounter <= 20)
BEGIN 
BEGIN TRAN

DECLARE @LocalCounter INT = 0

    WHILE (@LocalCounter <= @NumRows)
    BEGIN
    INSERT TestGuidA (SomeDate,batchNumber) VALUES (GETDATE(),@BatchCounter)
    SET @LocalCounter +=1
    END

SET @LocalCounter = 0

    WHILE (@LocalCounter <= @NumRows)
    BEGIN
    INSERT TestGuidB (SomeDate,batchNumber) VALUES (GETDATE(),@BatchCounter)
    SET @LocalCounter +=1
    END

SET @BatchCounter +=1
COMMIT 
END
-------------------------------
use master
DBCC FREEPROCCACHE -- Clear plan cache for next text. 

1

Обычно фрагментация индекса происходит, когда есть Updateили Insertоперация происходит на столе.

Если вы хотите быстро вызвать проблему (фрагментация индекса), создайте Indexв своей тестовой таблице меньше fill factorи выполните тяжелую операцию Updateили Insertоперацию с этой таблицей. Вы можете работать с этими скриптами ..


1

Вы также можете использовать, CRYPT_GEN_RANDOMкак я сделал в этом ответе: Схема фильтра в сценарии оптимизации индекса .

Вы можете вставить данные в числовой столбец с индексом для его фрагментации следующим образом:

-- Fill with random integers to create fragmentation
INSERT INTO [ProdTable] (c1, c2) VALUES  (CRYPT_GEN_RANDOM(8000), 'filler');
GO 12800

Вы также можете обновить данные или преобразовать в строку вместо числа, если это то, что вам нужно.

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.