ทำไมค่าใช้จ่ายโดยประมาณของ (เหมือนกัน) 1,000 ค้นหาในดัชนีที่ไม่ซ้ำกันแตกต่างกันในแผนเหล่านี้


27

ในแบบสอบถามด้านล่างแผนการดำเนินการทั้งคู่คาดว่าจะทำการค้นหา 1,000 ครั้งในดัชนีที่ไม่ซ้ำกัน

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

ทั้งลูปซ้อนกันมี <NestedLoops Optimized="false" WithOrderedPrefetch="true">

ใครรู้ว่าทำไมงานนี้มีราคาอยู่ที่ 0.172434 ในแผนแรก แต่ 3.01702 ต่อวินาที

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

ติดตั้ง

CREATE TABLE dbo.Target(KeyCol int PRIMARY KEY, OtherCol char(32) NOT NULL);

CREATE TABLE dbo.Staging(KeyCol int PRIMARY KEY, OtherCol char(32) NOT NULL); 

INSERT INTO dbo.Target
SELECT TOP (1000000) ROW_NUMBER() OVER (ORDER BY @@SPID), LEFT(NEWID(),32)
FROM master..spt_values v1,  
     master..spt_values v2;

INSERT INTO dbo.Staging
SELECT TOP (1000) ROW_NUMBER() OVER (ORDER BY @@SPID), LEFT(NEWID(),32)
FROM master..spt_values v1;

แบบสอบถาม 1 ลิงก์ "วางแผน"

WITH T
     AS (SELECT *
         FROM   Target AS T
         WHERE  T.KeyCol IN (SELECT S.KeyCol
                             FROM   Staging AS S))
MERGE T
USING Staging S
ON ( T.KeyCol = S.KeyCol )
WHEN NOT MATCHED THEN
  INSERT ( KeyCol, OtherCol )
  VALUES(S.KeyCol, S.OtherCol )
WHEN MATCHED AND T.OtherCol > S.OtherCol THEN
  UPDATE SET T.OtherCol = S.OtherCol;

ข้อความค้นหา 2 "วางแผน" ลิงก์

MERGE Target T
USING Staging S
ON ( T.KeyCol = S.KeyCol )
WHEN NOT MATCHED THEN
  INSERT ( KeyCol, OtherCol )
  VALUES( S.KeyCol, S.OtherCol )
WHEN MATCHED AND T.OtherCol > S.OtherCol THEN
  UPDATE SET T.OtherCol = S.OtherCol; 

แบบสอบถาม 1

แบบสอบถาม 2

การทดสอบข้างต้นบน SQL Server 2014 (SP2) (KB3171021) - 12.0.5000.0 (X64)


@Joe Obbishชี้ให้เห็นในความคิดเห็นที่เรียบง่ายกว่า

SELECT *
FROM staging AS S 
  LEFT OUTER JOIN Target AS T 
    ON T.KeyCol = S.KeyCol;

VS

SELECT *
FROM staging AS S 
  LEFT OUTER JOIN (SELECT * FROM Target) AS T 
    ON T.KeyCol = S.KeyCol;

สำหรับตารางการจัดเตรียม 1,000 แถวทั้งสองข้างต้นยังคงมีรูปร่างแผนเหมือนกันพร้อมลูปซ้อนกันและแผนที่ไม่มีตารางที่ได้รับปรากฏราคาถูกกว่า แต่สำหรับตารางการจัดเตรียม 10,000 แถวและตารางเป้าหมายเดียวกันกับตารางด้านบน รูปร่าง (พร้อมการสแกนเต็มรูปแบบและการรวมเข้าด้วยกันดูเหมือนจะค่อนข้างน่าสนใจกว่าการค้นหาที่มีราคาแพง) การแสดงความแตกต่างของค่าใช้จ่ายนี้อาจมีความหมายอื่นนอกเหนือจากการทำให้แผนเปรียบเทียบยากขึ้น

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

คำตอบ:


20

ใครรู้ว่าทำไมงานนี้มีราคาอยู่ที่ 0.172434 ในแผนแรก แต่ 3.01702 ต่อวินาที

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

มีอินพุตการคิดต้นทุนอีกรายการหนึ่งคือSmart Seek การคิดต้นทุนซึ่งเป็นที่ทราบรายละเอียดเล็กน้อย การคาดเดาของฉัน (และนั่นคือทั้งหมดที่อยู่ในขั้นตอนนี้) คือ SSC พยายามประเมินค่าใช้จ่าย I / O ด้านในอย่างละเอียดโดยอาจพิจารณาการสั่งซื้อในท้องถิ่นและ / หรือช่วงของค่าที่จะดึงข้อมูล ใครจะรู้.

ตัวอย่างเช่นการดำเนินการค้นหาครั้งแรกไม่เพียง แต่นำไปสู่แถวที่ร้องขอ แต่แถวทั้งหมดในหน้านั้น (ตามลำดับดัชนี) เมื่อกำหนดรูปแบบการเข้าถึงโดยรวมการดึงข้อมูล 1,000 แถวในการค้นหา 1,000 ครั้งต้องใช้การอ่านทางกายภาพ 2 ครั้งเท่านั้นแม้จะปิดการอ่านล่วงหน้าและปิดการดึงข้อมูลล่วงหน้า จากมุมมองดังกล่าวการคิดต้นทุน I / O เริ่มต้นแสดงให้เห็นถึงการประเมินค่าสูงไปอย่างมีนัยสำคัญและต้นทุนที่ปรับด้วย SSC นั้นใกล้เคียงกับความเป็นจริงมากขึ้น

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

เป็นเรื่องน่าเสียดายที่ Compute Scalar สำหรับการคาดการณ์ด้านนอกแบบสอบถามดูเหมือนว่าจะรบกวน SSC ที่นี่ การคำนวณสเกลาร์มักจะถูกย้ายไปอยู่เหนือการเข้าร่วม แต่สิ่งเหล่านี้ต้องอยู่ในที่ที่พวกเขาอยู่ ถึงกระนั้น Compute Scalars ปกติส่วนใหญ่นั้นค่อนข้างโปร่งใสในการปรับให้เหมาะสมดังนั้นมันจึงค่อนข้างน่าแปลกใจ

โดยไม่คำนึงถึงเมื่อการดำเนินการทางกายภาพPhyOp_Rangeถูกสร้างขึ้นจากการเลือกอย่างง่ายในดัชนีSelIdxToRngSSC จะมีประสิทธิภาพ เมื่อมีการใช้งานที่ซับซ้อนมากขึ้นSelToIdxStrategy(การเลือกในตารางไปยังกลยุทธ์ดัชนี) ผลลัพธ์PhyOp_Rangeจะรัน SSC แต่ผลลัพธ์จะไม่ลดลง อีกครั้งดูเหมือนว่าการดำเนินการโดยตรงที่ง่ายและตรงกว่านั้นจะทำงานได้ดีที่สุดกับ SSC

ฉันหวังว่าฉันจะบอกคุณได้อย่างแม่นยำว่า SSC ทำอะไรและแสดงการคำนวณที่แน่นอน แต่ฉันไม่ทราบรายละเอียดเหล่านั้น หากคุณต้องการสำรวจเอาต์พุตการติดตามแบบ จำกัด ที่มีอยู่สำหรับตัวคุณเองคุณสามารถใช้ค่าสถานะการติดตามที่ไม่มีเอกสาร 2398 เอาต์พุตตัวอย่างคือ:

การหาต้นทุนอย่างชาญฉลาด (7.1) :: 1.34078e + 154, 0.001

ตัวอย่างนั้นเกี่ยวข้องกับบันทึกกลุ่ม 7 ทางเลือก 1 แสดงขอบเขตต้นทุนสูงสุดและปัจจัย 0.001 หากต้องการดูปัจจัยที่สะอาดกว่าโปรดสร้างตารางใหม่โดยไม่ขนานกันเพื่อให้หน้าเว็บมีความหนาแน่นมากที่สุด โดยไม่ต้องทำเช่นนั้นปัจจัยจะมีค่ามากกว่า 0.000821 สำหรับตารางเป้าหมายตัวอย่างของคุณ แน่นอนว่ามีความสัมพันธ์ที่ชัดเจนพอสมควร

SSC ยังสามารถปิดการใช้งานด้วยค่าสถานะการสืบค้นกลับที่ไม่มีเอกสาร 2399 ด้วยค่าสถานะที่ใช้งานค่าใช้จ่ายทั้งสองเป็นค่าที่สูงขึ้น


8

ไม่แน่ใจว่านี่เป็นคำตอบ แต่มันยาวไปหน่อยสำหรับความคิดเห็น สาเหตุของความแตกต่างคือการคาดเดาที่บริสุทธิ์ในส่วนของฉันและอาจเป็นอาหารสำหรับคนอื่น

การสืบค้นที่ง่ายขึ้นด้วยแผนการดำเนินการ

SELECT S.KeyCol, 
       S.OtherCol,
       T.*
FROM staging AS S 
  LEFT OUTER JOIN Target AS T 
    ON T.KeyCol = S.KeyCol;

SELECT S.KeyCol, 
       S.OtherCol,
       T.*
FROM staging AS S 
  LEFT OUTER JOIN (
                  SELECT *
                  FROM Target
                  ) AS T 
    ON T.KeyCol = S.KeyCol;

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

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

ฉันเดาว่าการมีอยู่ของสเกลาร์คำนวณคือสิ่งที่ทำให้ต้นทุน IO สำหรับการสืบค้นที่สอง

จากภายในเครื่องมือเพิ่มประสิทธิภาพ: การคิดต้นทุนตามแผน

ต้นทุน CPU คำนวณเป็น 0.0001581 สำหรับแถวแรกและ 0.000011 สำหรับแถวถัดไป
...
ค่าใช้จ่าย I / O ของ 0.003125 เท่ากับ 1/320 - สะท้อนให้เห็นถึงรูปแบบของสมมติฐานที่ว่าระบบย่อยของดิสก์สามารถดำเนินการสุ่ม I / O 320 ต่อวินาที
...
ส่วนประกอบการคิดต้นทุนนั้นฉลาดพอที่จะรับรู้ว่าจำนวนทั้งหมดของ เพจที่ต้องนำเข้ามาจากดิสก์ต้องไม่เกินจำนวนหน้าที่ต้องการในการจัดเก็บทั้งตาราง

ในกรณีของฉันตารางใช้ 5618 หน้าและรับ 1,000 แถวจาก 1000000 แถวจำนวนหน้าที่ต้องการโดยประมาณคือ 5.618 ให้ต้นทุน IO เท่ากับ 0.015625

ค่าใช้จ่าย CPU สำหรับการสืบค้นทั้งสองแบบจะเท่ากัน, 0.0001581 * 1000 executions = 0.1581.

ดังนั้นตามบทความที่ลิงก์ด้านบนเราสามารถคำนวณค่าใช้จ่ายสำหรับการสืบค้นแรกให้เป็น 0.173725

และสมมติว่าฉันถูกต้องเกี่ยวกับวิธีการคำนวณสเกลาร์ทำให้เกิดความยุ่งเหยิงของ IO Cost มันสามารถคำนวณได้ถึง 3.2831

ไม่ตรงกับที่แสดงในแผน แต่อยู่ในบริเวณใกล้เคียง


6

(นี่น่าจะดีกว่าเป็นความเห็นต่อคำตอบของ Paul แต่ฉันยังไม่มีตัวแทนเพียงพอ)

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

ครั้งแรกฉันได้ดูบันทึกสุดท้ายเพื่อดูว่ามีการใช้ตัวดำเนินการทางกายภาพอย่างไร แน่นอนว่าพวกเขาดูเหมือนกันตามแผนการดำเนินการกราฟิก ดังนั้นฉันใช้แฟล็กการติดตาม3604และ8615สิ่งแรกนำเอาต์พุตไปยังไคลเอ็นต์และอันดับที่สองเผยให้เห็นบันทึกสุดท้าย:

SELECT S.*, T.KeyCol
FROM Staging AS S
      LEFT OUTER JOIN Target AS T
       ON T.KeyCol = S.KeyCol
OPTION(QUERYTRACEON 3604, -- Output client info
       QUERYTRACEON 8615, -- Shows Final Memo structure
       RECOMPILE);

ติดตามกลับจากRoot GroupฉันพบPhyOp_Rangeผู้ประกอบการเหล่านี้เกือบเหมือนกัน:

  1. PhyOp_Range 1 ASC 2.0 Cost(RowGoal 0,ReW 0,ReB 999,Dist 1000,Total 1000)= 0.175559(Distance = 2)
  2. PhyOp_Range 1 ASC 3.0 Cost(RowGoal 0,ReW 0,ReB 999,Dist 1000,Total 1000)= 3.01702(Distance = 2)

ข้อแตกต่างที่ชัดเจนสำหรับฉันคือ2.0และ3.0ซึ่งอ้างถึง "กลุ่มบันทึก 2 ของพวกเขาดั้งเดิม" และ "กลุ่มบันทึก 3 ดั้งเดิม" ของพวกเขา เมื่อตรวจสอบบันทึกแล้วสิ่งเหล่านี้อ้างถึงสิ่งเดียวกัน - ดังนั้นยังไม่มีความแตกต่างที่เปิดเผย

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

 SELECT S.*, T.KeyCol
 FROM Staging AS S
      LEFT OUTER JOIN Target AS T
        ON T.KeyCol = S.KeyCol
 OPTION (QUERYTRACEON 3604, -- Output info to client
         QUERYTRACEON 2363, -- Show stats and cardinality info
         QUERYTRACEON 8675, -- Show optimization process info
         QUERYTRACEON 8606, -- Show logical query trees
         QUERYTRACEON 8607, -- Show physical query tree
         QUERYTRACEON 2372, -- Show memory utilization info for optimization stages 
         QUERYTRACEON 2373, -- Show memory utilization info for applying rules
         RECOMPILE );

ประการที่สามฉันดูว่ากฎใดที่นำไปใช้กับเราPhyOp_Rangeที่มีลักษณะคล้ายกันมาก ผมใช้ธงคู่ร่องรอยกล่าวโดยพอลในบล็อกโพสต์

SELECT S.*, T.KeyCol
FROM Staging AS S
      LEFT OUTER JOIN (SELECT KeyCol
                      FROM Target) AS T
       ON T.KeyCol = S.KeyCol
OPTION (QUERYTRACEON 3604, -- Output info to client
        QUERYTRACEON 8619, -- Show applied optimization rules
        QUERYTRACEON 8620, -- Show rule-to-memo info
        QUERYTRACEON 8621, -- Show resulting tree
        QUERYTRACEON 2398, -- Show "smart seek costing"
        RECOMPILE );

จากการส่งออกเราจะเห็นว่าโดยตรงและJOINใช้กฎนี้จะได้รับของเราผู้ประกอบการ:PhyOp_Range Rule Result: group=7 2 <SelIdxToRng>PhyOp_Range 1 ASC 2 (Distance = 2)subselect Rule Result: group=9 2 <SelToIdxStrategy>PhyOp_Range 1 ASC 3 (Distance = 2)ใช้กฎนี้แทน: นี่คือที่ที่คุณเห็นข้อมูล "การแสวงหาการคิดต้นทุนอย่างฉลาด" ที่เกี่ยวข้องกับแต่ละกฎ สำหรับโดยตรงและJOINนี่คือการส่งออก Smart seek costing (7.2) :: 1.34078e+154 , 0.001(สำหรับฉัน): สำหรับ subselect Smart seek costing (9.2) :: 1.34078e+154 , 1นี้เป็นเอาท์พุท:

ในท้ายที่สุดฉันไม่สามารถสรุปได้มากนัก - แต่คำตอบของพอลเป็นการปิดช่องว่างเกือบทั้งหมด ฉันต้องการดูข้อมูลเพิ่มเติมเกี่ยวกับการหาต้นทุนอย่างชาญฉลาด


4

นี่ไม่ใช่คำตอบจริงๆ - อย่างที่ Mikael สังเกตไว้มันยากที่จะพูดถึงปัญหานี้ในความคิดเห็น ...

ที่น่าสนใจถ้าคุณแปลงข้อความค้นหาย่อย(select KeyCol FROM Target)เป็น TVF แบบอินไลน์คุณจะเห็นแผนและค่าใช้จ่ายนั้นเป็นข้อความค้นหาเดียวกันกับข้อความค้นหาดั้งเดิมง่ายๆ:

CREATE FUNCTION dbo.cs_test()
RETURNS TABLE
WITH SCHEMABINDING
AS 
RETURN (
    SELECT KeyCol FROM dbo.Target
    );

/* "normal" variant */
SELECT S.KeyCol, s.OtherCol, T.KeyCol 
FROM staging AS S 
    LEFT OUTER JOIN Target AS T ON T.KeyCol = S.KeyCol;

/* "subquery" variant */
SELECT S.KeyCol, s.OtherCol, T.KeyCol 
FROM staging AS S 
    LEFT OUTER JOIN (SELECT KeyCol FROM Target) AS T ON T.KeyCol = S.KeyCol;

/* "inline-TVF" variant */
SELECT S.KeyCol, s.OtherCol, T.KeyCol 
FROM staging AS S 
    LEFT OUTER JOIN dbo.cs_test() t ON s.KeyCol = t.Keycol

แผนแบบสอบถาม ( ลิงก์วางทับ ):

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

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

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

SELECT S.KeyCol, s.OtherCol, T.KeyCol 
FROM staging AS S 
    LEFT OUTER JOIN (
        SELECT KeyCol = CHECKSUM(NEWID()) 
        FROM Target
        ) AS T ON T.KeyCol = S.KeyCol;

วิธีจะคุณเสียค่าใช้จ่ายที่? เครื่องมือเพิ่มประสิทธิภาพคิวรีเลือกแผนคล้ายกันมากกับตัวแปร "แบบสอบถามย่อย" ด้านบนซึ่งมีสเกลาร์คำนวณ ( ลิงก์ethetheplan.com ):

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

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

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

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

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