ฉันจะแยกส่วนดัชนีเซิร์ฟเวอร์ SQL โดยเจตนาได้อย่างไร


9

ฉันต้องการสร้างเงื่อนไขดัชนีที่ไม่ดีอย่างตั้งใจบนฐานข้อมูลการทดสอบ SQL Server 2017 ที่ฉันมีเพื่อทำความเข้าใจสคริปต์การบำรุงรักษาเหล่านี้ให้ดีขึ้นหรือไม่ ดัชนีเซิร์ฟเวอร์ SQL และการบำรุงรักษาสถิติ

มีวิธีที่รวดเร็ว / อัตโนมัติในการลดความสมบูรณ์ของดัชนีหรือเพิ่มการกระจายตัวของดัชนี? คุณรู้หรือไม่ว่าทรัพยากรที่มีประโยชน์ใด ๆ ที่ฉันสามารถทำได้เพื่อบรรลุเป้าหมายนี้?


ทั้งนี้ขึ้นอยู่กับคำจำกัดความของคุณน่าเกลียดคุณอาจต้องการเลอะปัจจัยเติมเช่นกันซึ่งจะไม่เลอะการแยกส่วน แต่จะมีผลกระทบต่อการย่อยสลาย
scsimon

3
คุณต้องการดัชนีหนึ่งดัชนีหรือดัชนีทั้งหมดในฐานข้อมูลหรือไม่? ถ้าคุณต้องการดัชนีทั้งหมดให้ลดขนาดฐานข้อมูลของคุณ -DBCC SHRINKDATABASE ([yourNONProdDB])
Kin Shah

ดัชนี db ทั้งหมดจะสมบูรณ์แบบ ขอบคุณ @KinShah
mororo

คำตอบ:


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สำหรับตัวอย่างที่ฉันจับด้วยกันสำหรับคำถามอื่น การใช้แถวความยาวผันแปรและอัปเดตโดยการสุ่มอาจทำให้เกิดการแยกส่วน
David Spillett

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เป็นที่ฉันได้ในคำตอบนี้: กรอง Schema ในดัชนีเพิ่มประสิทธิภาพสคริปต์

คุณสามารถแทรกข้อมูลในคอลัมน์ตัวเลขที่มีดัชนีอยู่เพื่อแยกส่วนดังนี้:

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

คุณสามารถอัปเดตข้อมูลหรือแปลงเป็นสตริงแทนที่จะเป็นตัวเลขหากนั่นคือสิ่งที่คุณต้องการ

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.