วิธีการใบ้การเข้าร่วมหลายคนใน SQL Server


9

ฉันมี 3 "ใหญ่" ตารางที่เข้าร่วมในคอลัมน์ (ทั้งสองint)

  • ตารางที่ 1 มีประมาณ 200 ล้านแถว
  • ตารางที่ 2 มีประมาณ 1.5 ล้านแถว
  • ตารางที่ 3 มีประมาณ 6 ล้านแถว

แต่ละตารางมีดัชนีคลัสเตอร์บนKey1, Key2และจากนั้นหนึ่งคอลัมน์อื่น ๆ Key1มี cardinality ต่ำและเบ้มาก มันถูกอ้างอิงอยู่เสมอในWHEREข้อ Key2ไม่เคยถูกกล่าวถึงในWHEREข้อ การเข้าร่วมแต่ละครั้งมีหลายต่อหลายคน

ปัญหาเกิดขึ้นจากการประมาณเชิงปริมาณ การประมาณผลลัพธ์ของการรวมแต่ละครั้งจะเล็กลงแทนที่จะใหญ่ขึ้น ซึ่งจะส่งผลในการประมาณการขั้นสุดท้ายของหลายร้อยต่ำเมื่อผลลัพธ์ที่แท้จริงเป็นล้าน

มีวิธีใดบ้างที่ฉันจะรู้ว่า CE เป็นตัวประมาณการที่ดีกว่า?

SELECT 1
FROM Table1 t1
     JOIN Table2 t2
       ON t1.Key1 = t2.Key1
          AND t1.Key2 = t2.Key2
     JOIN Table3 t3
       ON t1.Key1 = t3.Key1
          AND t1.Key2 = t3.Key2
WHERE t1.Key1 = 1;

วิธีแก้ปัญหาที่ฉันได้ลอง:

  • สร้างสถิติหลายคอลัมน์ในKey1,Key2
  • สร้างตันสถิติกรองบนKey1(ซึ่งจะช่วยให้ไม่น้อย แต่ผมจบลงด้วยการพันของสถิติผู้ใช้สร้างขึ้นในฐานข้อมูล.)

แผนปฏิบัติการสวมหน้ากาก (ขออภัยสำหรับการปิดบังไม่ดี)

ในกรณีที่ฉันดูผลลัพธ์จะมี 9 ล้านแถว CE ใหม่ประมาณ 180 แถว มรดก CE ประมาณ 6100 แถว

นี่คือตัวอย่างที่ทำซ้ำได้:

DROP TABLE IF EXISTS #Table1, #Table2, #Table3;
CREATE TABLE #Table1 (Key1 INT NOT NULL, Key2 INT NOT NULL, T1Key3 INT NOT NULL, CONSTRAINT pk_t1 PRIMARY KEY CLUSTERED (Key1, Key2, T1Key3));
CREATE TABLE #Table2 (Key1 INT NOT NULL, Key2 INT NOT NULL, T2Key3 INT NOT NULL, CONSTRAINT pk_t2 PRIMARY KEY CLUSTERED (Key1, Key2, T2Key3));
CREATE TABLE #Table3 (Key1 INT NOT NULL, Key2 INT NOT NULL, T3Key3 INT NOT NULL, CONSTRAINT pk_t3 PRIMARY KEY CLUSTERED (Key1, Key2, T3Key3));

-- Table1 
WITH Numbers
     AS (SELECT TOP (1000000) Number = ROW_NUMBER() OVER(ORDER BY t1.number)
         FROM master..spt_values t1
              CROSS JOIN master..spt_values t2),
     DataSize (Key1, NumberOfRows)
     AS (SELECT 1, 2000 UNION
         SELECT 2, 10000 UNION
         SELECT 3, 25000 UNION
         SELECT 4, 50000 UNION
         SELECT 5, 200000)
INSERT INTO #Table1
SELECT Key1
     , Key2 = ROW_NUMBER() OVER (PARTITION BY Key1, T1Key3 ORDER BY Number)
     , T1Key3
FROM DataSize
     CROSS APPLY (SELECT TOP(NumberOfRows) 
                         Number
                       , T1Key3 = Number%(Key1*Key1) + 1 
                  FROM Numbers
                  ORDER BY Number) size;

-- Table2 (same Key1, Key2 values; smaller number of distinct third Key)
WITH Numbers
     AS (SELECT TOP (1000000) Number = ROW_NUMBER() OVER(ORDER BY t1.number)
         FROM master..spt_values t1
              CROSS JOIN master..spt_values t2)
INSERT INTO #Table2
SELECT DISTINCT 
       Key1
     , Key2
     , T2Key3
FROM #Table1
     CROSS APPLY (SELECT TOP (Key1*10) 
                         T2Key3 = Number
                  FROM Numbers
                  ORDER BY Number) size;

-- Table2 (same Key1, Key2 values; smallest number of distinct third Key)
WITH Numbers
     AS (SELECT TOP (1000000) Number = ROW_NUMBER() OVER(ORDER BY t1.number)
         FROM master..spt_values t1
              CROSS JOIN master..spt_values t2)
INSERT INTO #Table3
SELECT DISTINCT 
       Key1
     , Key2
     , T3Key3
FROM #Table1
     CROSS APPLY (SELECT TOP (Key1) 
                         T3Key3 = Number
                  FROM Numbers
                  ORDER BY Number) size;


DROP TABLE IF EXISTS #a;
SELECT col = 1 
INTO #a
FROM #Table1 t1
     JOIN #Table2 t2
       ON t1.Key1 = t2.Key1
          AND t1.Key2 = t2.Key2
WHERE t1.Key1 = 1;

DROP TABLE IF EXISTS #b;
SELECT col = 1 
INTO #b
FROM #Table1 t1
     JOIN #Table2 t2
       ON t1.Key1 = t2.Key1
          AND t1.Key2 = t2.Key2
     JOIN #Table3 t3
       ON t1.Key1 = t3.Key1
          AND t1.Key2 = t3.Key2
WHERE t1.Key1 = 1;

คำตอบ:


5

เพื่อให้ชัดเจนเครื่องมือเพิ่มประสิทธิภาพรู้อยู่แล้วว่าเป็นการเข้าร่วมแบบกลุ่มต่อกลุ่ม หากคุณบังคับให้รวมการเข้าร่วมและดูที่แผนที่โดยประมาณคุณสามารถเห็นสถานที่ให้บริการสำหรับผู้ประกอบการเข้าร่วมซึ่งจะบอกคุณว่าการเข้าร่วมอาจเป็นหลายต่อหลายคน ปัญหาที่คุณต้องแก้ไขที่นี่คือกระแทกค่าประมาณของ cardinality สมมุติว่าคุณได้รับแผนแบบสอบถามที่มีประสิทธิภาพมากขึ้นสำหรับส่วนของแบบสอบถามที่คุณไม่ได้ใช้

สิ่งแรกที่ฉันจะลองคือการใส่ผลลัพธ์ของการเข้าร่วมจากObject3และObject5ลงในตารางชั่วคราว สำหรับแผนที่คุณโพสต์เป็นเพียงคอลัมน์เดียวใน 51393 แถวดังนั้นแทบจะไม่ต้องใช้พื้นที่ใน tempdb คุณสามารถรวบรวมสถิติเต็มรูปแบบบนโต๊ะชั่วคราวและเพียงอย่างเดียวอาจเพียงพอที่จะได้รับการประเมินความแม่นยำเชิงหัวใจขั้นสุดท้ายที่เพียงพอ การรวบรวมสถิติเต็มในObject1อาจช่วยได้เช่นกัน การประเมินความผิดปกติมักจะแย่ลงเมื่อคุณสำรวจจากแผนจากขวาไปซ้าย

หากวิธีนี้ไม่ได้ผลคุณสามารถลองใช้ENABLE_QUERY_OPTIMIZER_HOTFIXESคำแนะนำการสืบค้นได้หากคุณยังไม่ได้เปิดใช้งานที่ระดับฐานข้อมูลหรือเซิร์ฟเวอร์ Microsoft ล็อกการแก้ไขผลกระทบต่อประสิทธิภาพการทำงานของแผนสำหรับ SQL Server 2016 หลังการตั้งค่านั้น บางคนเกี่ยวข้องกับการประเมินความสำคัญเชิงหัวใจดังนั้นบางทีคุณอาจโชคดีและการแก้ไขข้อใดข้อหนึ่งจะช่วยคุณได้ คุณสามารถลองใช้ตัวประมาณค่า cardinality เดิมด้วยFORCE_LEGACY_CARDINALITY_ESTIMATIONคำใบ้ ชุดข้อมูลบางอย่างอาจได้รับการประเมินที่ดีขึ้นกับ CE ดั้งเดิม

ในฐานะที่เป็นทางเลือกสุดท้ายคุณสามารถเพิ่มการประเมินระดับความเป็นMANY()หัวใจได้ด้วยตนเองไม่ว่าจะด้วยปัจจัยใดก็ตามที่คุณต้องการโดยใช้ฟังก์ชั่นของ Adam Machanic ฉันพูดถึงมันในคำตอบอื่นแต่ดูเหมือนว่าลิงค์จะตาย หากคุณสนใจฉันสามารถลองขุดอะไรซักอย่าง


make_parallelฟังก์ชั่นของอดัมเคยชินกับการช่วยบรรเทาปัญหา manyฉันจะมีลักษณะที่ ดูเหมือนว่าจะเป็นวงช่วยเหลือขั้นต้นสวย
Steven Hibble

2

สถิติ SQL Server มีฮิสโตแกรมสำหรับคอลัมน์นำของวัตถุสถิติเท่านั้น ดังนั้นคุณสามารถสร้างสถิติการกรองที่ให้ histogram ของค่าหาแต่เพียงในหมู่แถวที่มีKey2 Key1 = 1การสร้างสถิติที่กรองเหล่านี้ในแต่ละตารางจะแก้ไขการประมาณและนำไปสู่พฤติกรรมที่คุณคาดหวังสำหรับการทดสอบการทดสอบ: การเข้าร่วมใหม่แต่ละครั้งจะไม่ส่งผลต่อการประเมินความสำคัญเชิงหัวใจขั้นสุดท้าย (ยืนยันทั้งใน SQL 2016 SP1 และ SQL 2017)

-- Note: Add "WITH FULLSCAN" to each if you want a perfect 20,000 row estimate
CREATE STATISTICS st_#Table1 ON #Table1 (Key2) WHERE Key1 = 1
CREATE STATISTICS st_#Table2 ON #Table2 (Key2) WHERE Key1 = 1
CREATE STATISTICS st_#Table3 ON #Table3 (Key2) WHERE Key1 = 1

หากไม่มีสถิติที่ถูกกรองเหล่านี้ SQL Server จะใช้วิธีการวิเคราะห์พฤติกรรมแบบฮิวริสติกเพื่อประเมินความสำคัญของการเข้าร่วมของคุณ ต่อไปนี้เอกสารประกอบด้วยคำอธิบายระดับสูงที่ดีของบางส่วนของการวิเคราะห์พฤติกรรมที่ใช้ SQL Server: การเพิ่มประสิทธิภาพการวางแผนการค้นหาของคุณกับ SQL Server 2014 Cardinality ประมาณการ

ตัวอย่างเช่นการเพิ่มUSE HINT('ASSUME_JOIN_PREDICATE_DEPENDS_ON_FILTERS')คำใบ้ให้กับแบบสอบถามของคุณจะเปลี่ยนการแก้ปัญหาการรวมการเข้าร่วมเพื่อสมมติความสัมพันธ์บางอย่าง (แทนที่จะเป็นอิสระ) ระหว่างภาคKey1แสดงและภาคKey2แสดงเข้าร่วมซึ่งอาจเป็นประโยชน์ต่อแบบสอบถามของคุณ สำหรับแบบสอบถามทดสอบสุดท้ายคำใบ้นี้จะเพิ่มการประมาณเชิงปริมาณจาก1,175ถึง7,551แต่ก็ยังค่อนข้างขี้อายของการ20,000ประมาณแถวที่ถูกต้องที่สร้างด้วยสถิติที่กรอง

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

DROP TABLE IF EXISTS #Table1_extract, #Table2_extract, #Table3_extract, #c
-- Extract only the subset of rows that match the filter predicate
-- (Or better yet, extract only the subset of columns you need!)
SELECT * INTO #Table1_extract FROM #Table1 WHERE Key1 = 1
SELECT * INTO #Table2_extract FROM #Table2 WHERE Key1 = 1
SELECT * INTO #Table3_extract FROM #Table3 WHERE Key1 = 1
-- Now perform the join on those extracts, removing the filter predicate
SELECT col = 1
INTO #c 
FROM #Table1_extract t1
JOIN #Table2_extract t2
    ON t1.Key2 = t2.Key2
JOIN #Table3_extract t3
    ON t1.Key2 = t3.Key2

เราใช้สถิติที่กรองออกมาอย่างกว้างขวาง แต่เรากำหนดให้เป็นหนึ่งKey1ค่าต่อหนึ่งค่าในแต่ละตาราง ตอนนี้เรามีพวกเขาหลายพันคน
Steven Hibble

2
@StevenHibble จุดที่ดีที่สถิติการกรองนับพันอาจทำให้การจัดการยากขึ้น (เราได้เห็นแล้วว่ามันส่งผลเสียต่อเวลาในการรวบรวมแผน) อาจไม่เหมาะกับกรณีการใช้งานของคุณ แต่ฉันยังได้เพิ่ม #temp ตารางวิธีอื่นที่เราเคยประสบความสำเร็จมาแล้วหลายครั้ง
Geoff Patterson

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