พฤติกรรมแปลก ๆ พร้อมขนาดตัวอย่างสำหรับการอัพเดทสถิติ


25

ฉันได้รับการตรวจสอบเกณฑ์การสุ่มตัวอย่างด้วยการปรับปรุงสถิติใน SQL Server (2012) และสังเกตเห็นพฤติกรรมที่น่าสงสัย โดยทั่วไปจำนวนแถวตัวอย่างที่ดูเหมือนจะแตกต่างกันภายใต้สถานการณ์บางอย่าง - แม้จะมีชุดข้อมูลเดียวกัน

ฉันเรียกใช้แบบสอบถามนี้:

--Drop table if exists
IF (OBJECT_ID('dbo.Test')) IS NOT NULL DROP TABLE dbo.Test;

--Create Table for Testing
CREATE TABLE dbo.Test(Id INT IDENTITY(1,1) CONSTRAINT PK_Test PRIMARY KEY CLUSTERED, TextValue VARCHAR(20) NULL);

--Insert enough data so we have more than 8Mb (the threshold at which sampling kicks in)
INSERT INTO dbo.Test(TextValue) 
SELECT TOP 1000000 'blahblahblah'
FROM sys.objects a, sys.objects b, sys.objects c, sys.objects d;  

--Create Index on TextValue
CREATE INDEX IX_Test_TextValue ON dbo.Test(TextValue);

--Update Statistics without specifying how many rows to sample
UPDATE STATISTICS dbo.Test IX_Test_TextValue;

--View the Statistics
DBCC SHOW_STATISTICS('dbo.Test', IX_Test_TextValue) WITH STAT_HEADER;

เมื่อฉันดูผลลัพธ์ของ SHOW_STATISTICS ฉันพบว่า "การสุ่มตัวอย่างแถว" แตกต่างกันไปตามการดำเนินการเต็มรูปแบบแต่ละครั้ง (เช่นตารางที่ถูกดร็อปสร้างใหม่และ repopulated)

ตัวอย่างเช่น:

แถวสุ่มตัวอย่าง

  • 318618
  • 319240
  • 324198
  • 314154

ความคาดหวังของฉันคือตัวเลขนี้จะเหมือนกันทุกครั้งที่มีตารางเหมือนกัน โดยวิธีการที่ฉันไม่ได้รับพฤติกรรมนี้ถ้าฉันเพียงแค่ลบข้อมูลและใส่ใหม่

ไม่ใช่คำถามสำคัญ แต่ฉันสนใจที่จะเข้าใจว่าเกิดอะไรขึ้น


2
มีไฟล์กี่ไฟล์ในกลุ่มไฟล์ที่คุณใส่เข้าไป? ฉันลองสองสามครั้งในปี 2559 และทั้งสองครั้งที่ตารางถูกแบ่งออกเป็น 3584 หน้าด้วย 279 แถวและ 1 กับ 64 ขนาดตัวอย่างที่แตกต่างกันสองแบบที่ฉันเห็นคือ 314712 และ 315270 - ทวีคูณแน่นอน 279 คู่
Martin Smith

1
@JoeObbish - อ่านทั้งหน้า AFAIK เสมอดังนั้นฉันไม่แปลกใจเลย ด้วยเหตุผลบางอย่างฉันคิดว่าตัวเลขในคำถามนั้นไม่ตรงกับรูปแบบนั้น แต่การทำซ้ำคณิตศาสตร์ที่พวกเขาทำ 318618 = 1142*279, 319240 = 1144*279 + 64, 324198=1162*279และ314154=1126เพื่อให้ความแปรปรวนเป็นจำนวนหน้าตัวอย่าง
Martin Smith

@MartinSmith เพียงไฟล์เดียว - รูปที่ 279 เป็นสิ่งที่น่าสนใจฉันมักจะเข้าใจรูปแบบที่เกี่ยวข้อง
Matthew McGiffen

คำตอบ:


26

พื้นหลัง

ข้อมูลสำหรับวัตถุสถิติถูกรวบรวมโดยใช้คำสั่งของแบบฟอร์ม:

SELECT 
    StatMan([SC0], [SC1], [SB0000]) 
FROM 
(
    SELECT TOP 100 PERCENT 
        [SC0], [SC1], STEP_DIRECTION([SC0]) OVER (ORDER BY NULL) AS [SB0000]
    FROM 
    (
        SELECT 
            [TextValue] AS [SC0], 
            [Id] AS [SC1] 
        FROM [dbo].[Test] 
            TABLESAMPLE SYSTEM (2.223684e+001 PERCENT) 
            WITH (READUNCOMMITTED) 
    ) AS _MS_UPDSTATS_TBL_HELPER 
    ORDER BY 
        [SC0], 
        [SC1], 
        [SB0000] 
) AS _MS_UPDSTATS_TBL
OPTION (MAXDOP 1)

คุณสามารถรวบรวมคำแถลงนี้ด้วย Extended Events หรือ Profiler ( SP:StmtCompleted)

เคียวรีการสร้างสถิติมักเข้าถึงตารางฐาน (แทนที่จะเป็นดัชนีที่ไม่ใช่คลัสเตอร์) เพื่อหลีกเลี่ยงการรวมกลุ่มของค่าที่เกิดขึ้นตามธรรมชาติบนหน้าดัชนีที่ไม่ได้จัดกลุ่ม

จำนวนแถวที่สุ่มตัวอย่างขึ้นอยู่กับจำนวนหน้าทั้งหมดที่เลือกสำหรับการสุ่มตัวอย่าง แต่ละหน้าของตารางจะถูกเลือกหรือไม่ก็ได้ แถวทั้งหมดในหน้าเว็บที่เลือกมีส่วนร่วมในสถิติ

ตัวเลขสุ่ม

SQL Server ใช้ตัวสร้างตัวเลขสุ่มเพื่อตัดสินใจว่าหน้าเว็บมีคุณสมบัติหรือไม่ ตัวสร้างที่ใช้ในอินสแตนซ์นี้คือตัวสร้างตัวเลขสุ่ม Lehmerพร้อมค่าพารามิเตอร์ตามที่แสดงด้านล่าง:

X next = X seed * 7 5 mod (2 31 - 1)

ค่าของคำนวณจากผลรวมของ:Xseed

  • ส่วนจำนวนเต็มต่ำของbigintตารางฐาน( ) partition_idเช่น

    SELECT
        P.[partition_id] & 0xFFFFFFFF
    FROM sys.partitions AS P
    WHERE
        P.[object_id] = OBJECT_ID(N'dbo.Test', N'U')
        AND P.index_id = 1;
  • ค่าที่ระบุไว้ในREPEATABLEข้อ

    • สำหรับตัวอย่างUPDATE STATISTICSที่REPEATABLEคุ้มค่าเป็น 1
    • ค่านี้ถูกเปิดเผยในm_randomSeedองค์ประกอบของข้อมูลการดีบักภายในของวิธีการเข้าถึงที่แสดงในแผนการดำเนินการเมื่อเปิดใช้งานการตั้งค่าสถานะการสืบค้นกลับ 8666 เป็นต้น<Field FieldName="m_randomSeed" FieldValue="1" />

สำหรับ SQL Server 2012 การคำนวณนี้เกิดขึ้นในsqlmin!UnOrderPageScanner::StartScan:

mov     edx,dword ptr [rcx+30h]
add     edx,dword ptr [rcx+2Ch]

โดยที่หน่วยความจำที่[rcx+30h]มี ID พาร์ติชันต่ำ 32 บิตและหน่วยความจำที่[rcx+2Ch]มีREPEATABLEค่าที่ใช้งาน

ตัวสร้างหมายเลขสุ่มถูกเริ่มต้นในภายหลังในวิธีเดียวกันการโทรsqlmin!RandomNumGenerator::Initโดยที่คำสั่ง:

imul    r9d,r9d,41A7h

... คูณจำนวนเมล็ดด้วย41A7hex (16807 decimal = 7 5 ) ตามที่แสดงในสมการข้างต้น

ตัวเลขสุ่มไป (สำหรับแต่ละหน้า) จะถูกสร้างขึ้นโดยใช้รหัสพื้นฐานที่เหมือนกัน inlined sqlmin!UnOrderPageScanner::SetupSubScannerเข้า

Statman

สำหรับStatManแบบสอบถามตัวอย่างที่แสดงด้านบนหน้าเดียวกันจะถูกรวบรวมไว้สำหรับคำสั่ง T-SQL:

SELECT 
    COUNT_BIG(*) 
FROM dbo.Test AS T 
    TABLESAMPLE SYSTEM (2.223684e+001 PERCENT)  -- Same sample %
    REPEATABLE (1)                              -- Always 1 for statman
    WITH (INDEX(0));                            -- Scan base object

สิ่งนี้จะตรงกับผลลัพธ์ของ:

SELECT 
    DDSP.rows_sampled
FROM sys.stats AS S
CROSS APPLY sys.dm_db_stats_properties(S.[object_id], S.stats_id) AS DDSP
WHERE 
    S.[object_id] = OBJECT_ID(N'dbo.Test', N'U')
    AND S.[name] = N'IX_Test_TextValue';

ตัวเรือนขอบ

ผลลัพธ์อย่างหนึ่งของการใช้ตัวสร้างตัวเลขสุ่ม MINSTD Lehmer คือไม่ควรใช้ค่าเมล็ดเป็นศูนย์และ int.max เนื่องจากจะทำให้อัลกอริทึมสร้างลำดับศูนย์ (เลือกทุกหน้า)

รหัสตรวจจับศูนย์และใช้ค่าจากระบบ 'นาฬิกา' เป็นเมล็ดในกรณีนั้น มันไม่ได้ทำเช่นเดียวกันหากเมล็ดเป็น int.max ( 0x7FFFFFFF= 2 31 - 1)

เราสามารถสร้างสถานการณ์จำลองนี้ได้เนื่องจากการคำนวณเริ่มต้นของเมล็ดนั้นเป็นผลรวมของ 32 บิตต่ำของ ID พาร์ติชันและREPEATABLEค่า REPEATABLEค่าที่จะส่งผลให้เมล็ดเป็น int.max และดังนั้นทุกหน้าถูกเลือกสำหรับกลุ่มตัวอย่างคือ

SELECT
    0x7FFFFFFF - (P.[partition_id] & 0xFFFFFFFF)
FROM sys.partitions AS P
WHERE
    P.[object_id] = OBJECT_ID(N'dbo.Test', N'U')
    AND P.index_id = 1;

การทำงานเป็นตัวอย่างที่สมบูรณ์:

DECLARE @SQL nvarchar(4000) = 
    N'
    SELECT
        COUNT_BIG(*) 
    FROM dbo.Test AS T 
        TABLESAMPLE (0 PERCENT) 
        REPEATABLE (' +
        (
            SELECT TOP (1)
                CONVERT(nvarchar(11), 0x7FFFFFFF - P.[partition_id] & 0xFFFFFFFF)
            FROM sys.partitions AS P
            WHERE
                P.[object_id] = OBJECT_ID(N'dbo.Test', N'U')
                AND P.index_id = 1
        ) + ')
        WITH (INDEX(0));';

PRINT @SQL;
--EXECUTE (@SQL);

ที่จะเลือกทุกแถวในทุก ๆ หน้าTABLESAMPLEตามที่ระบุไว้ (แม้แต่ศูนย์เปอร์เซ็นต์)


11

นี่เป็นคำถามที่ยอดเยี่ยม! ฉันจะเริ่มต้นด้วยสิ่งที่ฉันรู้แน่นอนแล้วย้ายไปเก็งกำไร จำนวนมากรายละเอียดเกี่ยวกับเรื่องนี้ในโพสต์บล็อกของฉันที่นี่

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

หากคุณลบและแทรกข้อมูลhobt_idซ้ำจะยังคงเหมือนเดิม ตราบใดที่ข้อมูลถูกวางในลักษณะเดียวกันบนดิสก์ (การสแกนคำสั่งการจัดสรรส่งกลับผลลัพธ์เดียวกันในลำดับเดียวกัน) ดังนั้นข้อมูลตัวอย่างไม่ควรเปลี่ยนแปลง

คุณยังสามารถเปลี่ยนจำนวนแถวที่สุ่มตัวอย่างได้โดยสร้างดัชนีคลัสเตอร์ขึ้นใหม่บนตาราง ตัวอย่างเช่น:

UPDATE STATISTICS dbo.Test IX_Test_TextValue;

DBCC SHOW_STATISTICS('dbo.Test', IX_Test_TextValue) WITH STAT_HEADER; -- 273862 rows

ALTER INDEX PK_Test on Test REBUILD;

UPDATE STATISTICS dbo.Test IX_Test_TextValue;

DBCC SHOW_STATISTICS('dbo.Test', IX_Test_TextValue) WITH STAT_HEADER; -- 273320 rows

สำหรับสาเหตุที่เกิดขึ้นฉันเชื่อว่าเป็นเพราะ SQL Server สแกนดัชนีคลัสเตอร์แทนที่จะเป็นดัชนีที่ไม่ใช่คลัสเตอร์เมื่อรวบรวมสถิติตัวอย่างบนดัชนี ผมยังคิดว่ามีที่ซ่อนอยู่ (กับคนของเราติดตามซ่อนคำสั่งสถิติการปรับปรุง) ค่าสำหรับใช้กับREPEATABLE TABLESAMPLEฉันไม่ได้พิสูจน์อะไรเลย แต่อธิบายว่าทำไมฮิสโตแกรมและแถวของคุณทำการเปลี่ยนแปลงด้วยการสร้างดัชนีคลัสเตอร์ใหม่


3

ฉันลืมว่า TABLESAMPLE ทำงานอย่างไรในแง่ของการกำหนดความน่าจะเป็นแบบสุ่มต่อหน้า - Martin Smith

ฉันเห็นสิ่งนี้ในInside Microsoft SQL Server 2008: T-SQL Queryingโดย Itzik Ben-Gan และฉันไม่สามารถเพิ่มเป็นความคิดเห็นได้ดังนั้นฉันโพสต์ที่นี่ฉันคิดว่ามันน่าสนใจสำหรับผู้อื่นเช่นกัน:

ป้อนคำอธิบายรูปภาพที่นี่

ดูเพิ่มเติมการสุ่มตัวอย่างโดยใช้ TABLESAMPLEโดย Roji พี. โธมัส

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