วิธีเพิ่มประสิทธิภาพการสืบค้น T-SQL โดยใช้ Execution Plan


15

ฉันมีแบบสอบถาม SQL ที่ฉันใช้เวลาสองวันที่ผ่านมาพยายามเพิ่มประสิทธิภาพโดยใช้การทดลองและข้อผิดพลาดและแผนการดำเนินการ แต่ไม่มีประโยชน์ โปรดยกโทษให้ฉันที่ทำเช่นนี้ แต่ฉันจะโพสต์แผนการดำเนินการทั้งหมดที่นี่ ฉันได้ใช้ความพยายามในการสร้างชื่อตารางและคอลัมน์ในแบบสอบถามและแผนปฏิบัติการทั่วไปทั้งเพื่อความกระชับและเพื่อปกป้อง IP ของ บริษัท ของฉัน แผนปฏิบัติการสามารถเปิดได้กับSQL ยามแผน Explorer ที่

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

นี่คือแบบสอบถาม:

DECLARE @Param0 DATETIME     = '2013-07-29';
DECLARE @Param1 INT          = CONVERT(INT, CONVERT(VARCHAR, @Param0, 112))
DECLARE @Param2 VARCHAR(50)  = 'ABC';
DECLARE @Param3 VARCHAR(100) = 'DEF';
DECLARE @Param4 VARCHAR(50)  = 'XYZ';
DECLARE @Param5 VARCHAR(100) = NULL;
DECLARE @Param6 VARCHAR(50)  = 'Text3';

SET NOCOUNT ON

DECLARE @MyTableVar TABLE
(
    B_Var1_PK int,
    Job_Var1 varchar(512),
    Job_Var2 varchar(50)
)

INSERT INTO @MyTableVar (B_Var1_PK, Job_Var1, Job_Var2) 
SELECT B_Var1_PK, Job_Var1, Job_Var2 FROM [fn_GetJobs] (@Param1, @Param2, @Param3, @Param4, @Param6);

CREATE TABLE #TempTable
(
    TTVar1_PK INT PRIMARY KEY,
    TTVar2_LK VARCHAR(100),
    TTVar3_LK VARCHAR(50),
    TTVar4_LK INT,
    TTVar5 VARCHAR(20)
);

INSERT INTO #TempTable
SELECT DISTINCT
    T.T1_PK,
    T.T1_Var1_LK,
    T.T1_Var2_LK,
    MAX(T.T1_Var3_LK),
    T.T1_Var4_LK
FROM
    MyTable1 T
    INNER JOIN feeds.MyTable2 A ON A.T2_Var1 = T.T1_Var4_LK
    INNER JOIN @MyTableVar B ON B.Job_Var2 = A.T2_Var2 AND B.Job_Var1 = A.T2_Var3
GROUP BY T.T1_PK, T.T1_Var1_LK, T.T1_Var2_LK, T.T1_Var4_LK

-- This is the slow statement...
SELECT 
    CASE E.E_Var1_LK
        WHEN 'Text1' THEN T.TTVar2_LK + '_' + F.F_Var1
        WHEN 'Text2' THEN T.TTVar2_LK + '_' + F.F_Var2
        WHEN 'Text3' THEN T.TTVar2_LK
    END,
    T.TTVar4_LK,
    T.TTVar3_LK,
    CASE E.E_Var1_LK
        WHEN 'Text1' THEN F.F_Var1
        WHEN 'Text2' THEN F.F_Var2
        WHEN 'Text3' THEN T.TTVar5
    END,
    A.A_Var3_FK_LK,
    C.C_Var1_PK,
    SUM(CONVERT(DECIMAL(18,4), A.A_Var1) + CONVERT(DECIMAL(18,4), A.A_Var2))
FROM #TempTable T
    INNER JOIN TableA (NOLOCK) A ON A.A_Var4_FK_LK  = T.TTVar1_PK
    INNER JOIN @MyTableVar     B ON B.B_Var1_PK     = A.Job
    INNER JOIN TableC (NOLOCK) C ON C.C_Var2_PK     = A.A_Var5_FK_LK
    INNER JOIN TableD (NOLOCK) D ON D.D_Var1_PK     = A.A_Var6_FK_LK
    INNER JOIN TableE (NOLOCK) E ON E.E_Var1_PK     = A.A_Var7_FK_LK  
    LEFT OUTER JOIN feeds.TableF (NOLOCK) F ON F.F_Var1 = T.TTVar5
WHERE A.A_Var8_FK_LK = @Param1
GROUP BY
    CASE E.E_Var1_LK
        WHEN 'Text1' THEN T.TTVar2_LK + '_' + F.F_Var1
        WHEN 'Text2' THEN T.TTVar2_LK + '_' + F.F_Var2
        WHEN 'Text3' THEN T.TTVar2_LK
    END,
    T.TTVar4_LK,
    T.TTVar3_LK,
    CASE E.E_Var1_LK 
        WHEN 'Text1' THEN F.F_Var1
        WHEN 'Text2' THEN F.F_Var2
        WHEN 'Text3' THEN T.TTVar5
    END,
    A.A_Var3_FK_LK, 
    C.C_Var1_PK


IF OBJECT_ID(N'tempdb..#TempTable') IS NOT NULL
BEGIN
    DROP TABLE #TempTable
END
IF OBJECT_ID(N'tempdb..#TempTable') IS NOT NULL
BEGIN
    DROP TABLE #TempTable
END

สิ่งที่ฉันได้พบคือข้อความที่สาม (แสดงความคิดเห็นว่าช้า) เป็นส่วนที่ใช้เวลามากที่สุด ทั้งสองงบก่อนกลับมาเกือบจะทันที

แผนการดำเนินการมีให้บริการเป็น XML ที่ลิงค์นี้

ดีกว่าคลิกขวาแล้วบันทึกจากนั้นเปิดใน SQL Sentry Plan Explorer หรือซอฟต์แวร์การดูอื่น ๆ แทนที่จะเปิดในเบราว์เซอร์ของคุณ

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


2
สถิติของคุณจะถูกปิด ครั้งสุดท้ายที่คุณยกเลิกการแยกส่วนดัชนีหรืออัพเดตสถิติ นอกจากนี้ฉันจะพยายามใช้ตาราง temp แทนตัวแปร table @MyTableVar เนื่องจากเครื่องมือเพิ่มประสิทธิภาพไม่สามารถใช้สถิติกับตัวแปรตารางได้
Adam Haines

ขอบคุณสำหรับคำตอบของคุณอดัม การเปลี่ยน @MyTableVar เป็นตารางชั่วคราวไม่มีผลใด ๆ แต่เป็นเพียงจำนวนแถวเล็กน้อย (ซึ่งสามารถเห็นได้จากแผนการดำเนินการ) อะไรในแผนการดำเนินการแสดงว่าสถิติของฉันหายไปไหน มันบ่งบอกว่าดัชนีใดควรจัดโครงสร้างใหม่หรือสร้างใหม่และตารางใดที่ควรมีการอัพเดตสถิติ?
Neo

3
การเข้าร่วมแฮชที่ด้านล่างขวามีประมาณ 24,000 แถวในอินพุตบิลด์ แต่จริง ๆ แล้ว 3,285,620 ดังนั้นอาจทะลักเข้าtempdbมาได้ นั่นคือค่าประมาณของแถวที่เกิดจากการเข้าร่วมระหว่างTableAและ@MyTableVarออกไป นอกจากนี้จำนวนแถวที่เข้าสู่การเรียงลำดับนั้นยิ่งใหญ่กว่าที่คาดไว้มาก
Martin Smith

คำตอบ:


22

ก่อนที่จะหาคำตอบหลักมีซอฟต์แวร์สองชิ้นที่คุณต้องอัปเดต

การอัพเดตซอฟต์แวร์ที่จำเป็น

ที่แรกก็คือ SQL Server คุณกำลังเรียกใช้ SQL Server 2008 Service Pack 1 (สร้าง 2531) คุณควรได้รับการปรับปรุงอย่างน้อย Service Pack ปัจจุบัน (SQL Server 2008 Service Pack 3 - build 5500) โครงสร้างล่าสุดของ SQL Server 2008 ณ เวลาที่เขียนคือ Service Pack 3, Cumulative Update 12 (build 5844)

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

การวิเคราะห์แผนแบบสอบถาม

การประมาณความสำคัญของตัวแปรตารางนั้นถูกต้องขอบคุณการคอมไพล์ระดับคำสั่งใหม่:

การประมาณตัวแปรตาราง

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

เข้าร่วมประมาณแรก

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

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

ข้อความค้นหาของคุณได้รับการกำหนดพารามิเตอร์ด้วย คุณควรพิจารณาเพิ่มOPTION (RECOMPILE)ลงในคิวรีหากค่าพารามิเตอร์ต่างกันมีผลต่อแผนคิวรี คุณควรพิจารณาใช้งานต่อไปดังนั้นเครื่องมือเพิ่มประสิทธิภาพสามารถดูค่าของ@Param1เวลาในการรวบรวม หากไม่มีอะไรอื่นสิ่งนี้อาจช่วยให้เครื่องมือเพิ่มประสิทธิภาพสร้างการประเมินที่สมเหตุสมผลมากขึ้นสำหรับดัชนีการค้นหาที่แสดงด้านบนเนื่องจากตารางมีขนาดใหญ่มากและมีการแบ่งพาร์ติชัน นอกจากนี้ยังอาจเปิดใช้งานการกำจัดพาร์ติชันแบบคงที่

ลองแบบสอบถามอีกครั้งกับตารางชั่วคราวแทนของตัวแปรตารางและ OPTION (RECOMPILE)คุณควรลองผลลัพธ์ของการเข้าร่วมเป็นครั้งแรกในตารางชั่วคราวอื่นแล้วเรียกใช้แบบสอบถามที่เหลือ จำนวนแถวไม่ใช่ทั้งหมดที่มีขนาดใหญ่ (3,285,620) ดังนั้นควรมีความรวดเร็วพอสมควร เครื่องมือเพิ่มประสิทธิภาพจะมีการประมาณค่าเชิงการนับและสถิติการกระจายที่แน่นอนสำหรับผลลัพธ์ของการเข้าร่วม ด้วยโชคส่วนที่เหลือของแผนจะตกอยู่ในสถานที่อย่างสวยงาม

ทำงานจากคุณสมบัติที่แสดงในแผนแบบสอบถาม materializing จะเป็น:

SELECT
    A.A_Var7_FK_LK,
    A.A_Var4_FK_LK,
    A.A_Var6_FK_LK, 
    A.A_Var5_FK_LK,
    A.A_Var1,
    A.A_Var2,
    A.A_Var3_FK_LK
INTO #AnotherTempTable
FROM @MyTableVar AS B
JOIN TableA AS A
    ON A.Job = B.B_Var1_PK
WHERE
    A_Var8_FK_LK = @Param1;

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


ขอบคุณมากสำหรับคำตอบเชิงลึกนี้ ขออภัยใช้เวลาหนึ่งสัปดาห์ในการตอบกลับ - ฉันทำงานนี้ทุกวันสลับกับงานอื่น ฉันได้ดำเนินการตามคำแนะนำของคุณแล้วให้การเข้าร่วม TableA เป็น#AnotherTempTableจริง สิ่งนี้ดูเหมือนจะมีผลกระทบที่ดีที่สุด - คำแนะนำอื่น ๆ (ใช้ตาราง temp แทนตัวแปรตารางสำหรับ @MyTableVar และการใช้OPTION (RECOMPILE)ไม่มีผลมากหรืออย่างใดก็ตาม 'Anonymize' และ 'Post to SQLPerformance.com' ตัวเลือกใน SQL Sentry Plan Explorer นั้นยอดเยี่ยม - ฉันเพิ่งใช้ไปแล้ว: answer.sqlperformance.com/questions/1087
Neo

-6

ฉันสังเกตเห็นว่าควรมี PK บน @MyTableVar และยอมรับว่า #MyTableVar มักจะทำงานได้ดีขึ้น (โดยมีจำนวนแถวมากกว่า)

สภาพภายในข้อที่

   WHERE A.A_Var8_FK_LK = @Param1

ควรย้ายไปที่การเข้าร่วมภายใน A AND'ed เครื่องมือเพิ่มประสิทธิภาพนั้นไม่ฉลาดพอในประสบการณ์ของฉันในการทำสิ่งนี้ (ขออภัยไม่ได้ดูแผน) และมันสามารถสร้างความแตกต่างอย่างมาก

หากการเปลี่ยนแปลงเหล่านั้นไม่แสดงการปรับปรุงฉันจะสร้างตาราง temp อื่นของ A และทุกสิ่งที่รวมเข้ากับข้อ จำกัด (อย่างดี?) โดย A.A_Var8_FK_LK = @ Param1 หากการจัดกลุ่มนั้นสมเหตุสมผลสำหรับคุณ

จากนั้นสร้างดัชนีคลัสเตอร์บนตาราง temp นั้น (ก่อนหรือหลังการสร้าง) สำหรับเงื่อนไขการเข้าร่วมถัดไป

จากนั้นเข้าร่วมผลลัพธ์นั้นกับตาราง (F และ T) ที่เหลืออยู่

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

การติดตามสามารถแสดงการรั่วไหลของ tempdb ซึ่งอาจหรืออาจไม่มีผลกระทบที่รุนแรง

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

หากคุณกำลังทำสิ่งเหล่านี้จำนวนมากบางทีเครื่องมือเพิ่มประสิทธิภาพเชิงพาณิชย์น่าลอง (หรือทดลองใช้) และยังคงเป็นประสบการณ์การเรียนรู้ที่ดี


ใช่แล้ว. ทำให้แน่ใจว่าแถวที่ส่งคืนโดย A ถูก จำกัด โดยข้อ จำกัด มิฉะนั้นเครื่องมือเพิ่มประสิทธิภาพอาจเข้าร่วมก่อนและใช้ข้อ จำกัด ในภายหลัง ฉันจัดการกับสิ่งนี้ทุกวัน
crokusek

4
@crokusek คุณผิดไป เครื่องมือเพิ่มประสิทธิภาพของ SQL-Server นั้นค่อนข้างดีที่รู้ว่าการสืบค้นนั้นเทียบเท่ากัน (ไม่ว่าเงื่อนไขจะอยู่ที่ WHERE หรือ ON clause) เมื่อมีการเข้าร่วมภายใน
ypercubeᵀᴹ

6
คุณอาจพบว่าซีรี่ส์ของ Paul White ใน Query Optimizerมีประโยชน์
Martin Smith

มันเป็นนิสัยที่แย่มาก อาจเป็นไปได้สำหรับกรณีนี้ (ที่มีข้อ จำกัด อย่างหนึ่ง) แต่ฉันมาจากดินแดนของนักพัฒนาหลายคนซ้อนกันบนและเงื่อนไขตามที่ข้อ SQL Server ไม่ ได้อย่างต่อเนื่อง "ย้าย" พวกเขากลับไปเข้าร่วมสำหรับคุณ
crokusek

ยอมรับไม่ถูกต้องสำหรับด้านนอก (และรวมขวา) แต่เมื่อมีเพียง AND'd การแสดงออกภายในข้อที่สอดคล้องและแต่ละระยะแต่เพียงผู้เดียวกับผู้ที่เฉพาะเจาะจงรวมภายในระยะเวลาที่อาจได้อย่างปลอดภัยและมีความมั่นใจถูกย้ายไปยัง "กับ" สถานที่การเพิ่มประสิทธิภาพและการปฏิบัติที่ดีที่สุด (IMO) ไม่ว่าจะเป็นเงื่อนไขการเข้าร่วม "จริง" หรือเพียงข้อ จำกัด คงที่เป็นรองจากการเพิ่มประสิทธิภาพการทำงานที่มีขนาดใหญ่ ลิงค์นั้นมีไว้สำหรับกรณีเล็กน้อย ชีวิตจริงมีหลายอย่างที่เงื่อนไขกับการแปลง () และคณิตศาสตร์และทำให้ผู้สมัครที่ดีขึ้นจากการปฏิบัติที่ดีที่สุด
crokusek
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.