พารามิเตอร์การดมกลิ่นเทียบกับตัวแปรเทียบกับการคอมไพล์ vs ออปติไมซ์สำหรับ UNKNOWN


40

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

เพียงเพื่อยืนยันว่าเราไม่ได้รับผลบวกผิดพลาดเราได้ dbcc freeproccache ใน proc และรันซ้ำเพื่อดูว่าผลลัพธ์ที่ได้รับการปรับปรุงจะเหมือนเดิมหรือไม่ แต่เพื่อความประหลาดใจของเรา proc ดั้งเดิมยังคงทำงานช้า เราลองอีกครั้งด้วย WITH RECOMPILE ยังคงช้า (เราลองคอมไพล์อีกครั้งในการโทรไปยัง proc และภายใน proc itelf) เรายังรีสตาร์ทเซิร์ฟเวอร์ (เห็นได้ชัดว่ากล่อง dev)

ดังนั้นคำถามของฉันคือสิ่งนี้ ... พารามิเตอร์การดมกลิ่นสามารถตำหนิได้อย่างไรเมื่อเราได้รับแบบสอบถามช้าแบบเดียวกันกับแคชแผนว่างเปล่า ... ไม่ควรมีพารามิเตอร์ใด ๆ ในการดมกลิ่น ???

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

ในการทดสอบเพิ่มเติมเราพบว่าการแทรก OPTION (OPTIMIZE FOR UNKNOWN) บน internals ของ proc DIDจะได้รับแผนการปรับปรุงที่คาดไว้

ดังนั้นบางคนฉลาดกว่าฉันคุณช่วยบอกเบาะแสเกี่ยวกับสิ่งที่เกิดขึ้นเบื้องหลังเพื่อสร้างผลลัพธ์ประเภทนี้ได้หรือไม่?

ในบันทึกอื่น ๆ แผนช้ายังได้รับการยกเลิกก่อนด้วยเหตุผลGoodEnoughPlanFoundในขณะที่แผนเร็วไม่มีเหตุผลยกเลิกต้นในแผนจริง

สรุป

  • การสร้างตัวแปรจากพารามิเตอร์ขาเข้า (1 วินาที)
  • กับคอมไพล์ใหม่ (30+ วินาที)
  • dbcc freeproccache (30+ วินาที)
  • OPTION (ปรับให้เหมาะสมสำหรับ UKNOWN) (1 วินาที)

UPDATE:

ดูแผนปฏิบัติการช้าที่นี่: https://www.dropbox.com/s/cmx2lrsea8q8mr6/plan_slow.xml

ดูแผนการดำเนินการอย่างรวดเร็วที่นี่: https://www.dropbox.com/s/b28x6a01w7dxsed/plan_fast.xml

หมายเหตุ: ตาราง, schema, ชื่อวัตถุถูกเปลี่ยนเพื่อเหตุผลด้านความปลอดภัย

คำตอบ:


43

แบบสอบถามคือ

SELECT SUM(Amount) AS SummaryTotal
FROM   PDetail WITH(NOLOCK)
WHERE  ClientID = @merchid
       AND PostedDate BETWEEN @datebegin AND @dateend 

ตารางประกอบด้วยแถวที่ 103,129,000

แผนได้อย่างรวดเร็วมองขึ้นโดย ClientID กับกริยาที่เหลือในวัน แต่ต้องทำ 96 Amountการค้นหาเพื่อดึง <ParameterList>ส่วนในแผนมีดังนี้

        <ParameterList>
          <ColumnReference Column="@dateend" 
                           ParameterRuntimeValue="'2013-02-01 23:59:00.000'" />
          <ColumnReference Column="@datebegin" 
                           ParameterRuntimeValue="'2013-01-01 00:00:00.000'" />
          <ColumnReference Column="@merchid" 
                           ParameterRuntimeValue="(78155)" />
        </ParameterList>

แผนแบบช้าจะค้นหาตามวันที่และมีการค้นหาเพื่อประเมินเพรดิเคตที่เหลือบน ClientId และเพื่อดึงจำนวนเงิน (โดยประมาณ 1 เทียบกับจริง 7,388,383) <ParameterList>ส่วนคือ

        <ParameterList>
          <ColumnReference Column="@EndDate" 
                           ParameterCompiledValue="'2013-02-01 23:59:00.000'" 
                           ParameterRuntimeValue="'2013-02-01 23:59:00.000'" />
          <ColumnReference Column="@BeginDate" 
                           ParameterCompiledValue="'2013-01-01 00:00:00.000'"               
                           ParameterRuntimeValue="'2013-01-01 00:00:00.000'" />
          <ColumnReference Column="@ClientID" 
                           ParameterCompiledValue="(78155)" 
                           ParameterRuntimeValue="(78155)" />
        </ParameterList>

ในกรณีที่สองนี้ParameterCompiledValueเป็นไม่ว่างเปล่า SQL Server ดมกลิ่นค่าที่ใช้ในแบบสอบถามสำเร็จแล้ว

หนังสือ"การแก้ไขปัญหาเบื้องต้นในทางปฏิบัติของ SQL Server 2005"มีไว้เพื่อกล่าวถึงการใช้ตัวแปรโลคอล

การใช้ตัวแปรท้องถิ่นเพื่อกำจัดการดมกลิ่นพารามิเตอร์เป็นเคล็ดลับที่ใช้กันโดยทั่วไป แต่คำใบ้OPTION (RECOMPILE)และOPTION (OPTIMIZE FOR)คำแนะนำ ... โดยทั่วไปแล้วจะสวยงามกว่าและมีความเสี่ยงน้อยกว่า


บันทึก

ใน SQL Server 2005 การคอมไพล์ระดับคำสั่งอนุญาตให้มีการคอมไพล์ของแต่ละคำสั่งในกระบวนงานที่เก็บไว้ที่จะรอการตัดบัญชีจนกระทั่งก่อนการดำเนินการของแบบสอบถามแรก จากนั้นค่าของตัวแปรท้องถิ่นจะเป็นที่รู้จัก ในทางทฤษฎี SQL Server สามารถใช้ประโยชน์จากสิ่งนี้ในการดมกลิ่นค่าตัวแปรท้องถิ่นในลักษณะเดียวกับที่มันดมพารามิเตอร์ อย่างไรก็ตามเนื่องจากเป็นเรื่องปกติที่จะใช้ตัวแปรโลคัลเพื่อกำจัดพารามิเตอร์การดมกลิ่นใน SQL Server 7.0 และ SQL Server 2000+ การดมกลิ่นของตัวแปรโลคัลไม่ได้เปิดใช้งานใน SQL Server 2005 มันอาจถูกเปิดใช้งานใน SQL Server รุ่นต่อไปในอนาคต เหตุผลที่ใช้หนึ่งในตัวเลือกอื่น ๆ ที่ระบุไว้ในบทนี้หากคุณมีตัวเลือก


จากการทดสอบอย่างรวดเร็วด้วยเหตุนี้พฤติกรรมที่อธิบายไว้ข้างต้นจะยังคงเหมือนเดิมในปี 2008 และ 2012 และตัวแปรจะไม่ถูกดักจับสำหรับการคอมไพล์ที่รอการตัดบัญชี แต่เฉพาะเมื่อมีการใช้OPTION RECOMPILEคำใบ้อย่างชัดเจนเท่านั้น

DECLARE @N INT = 0

CREATE TABLE #T ( I INT );

/*Reference to #T means this statement is subject to deferred compile*/
SELECT *
FROM   master..spt_values
WHERE  number = @N
       AND EXISTS(SELECT COUNT(*) FROM #T)

SELECT *
FROM   master..spt_values
WHERE  number = @N
OPTION (RECOMPILE)

DROP TABLE #T 

แม้จะมีการคอมไพล์รอการตัดบัญชีตัวแปรจะไม่ถูกดมกลิ่นและจำนวนแถวโดยประมาณไม่ถูกต้อง

ค่าประมาณเทียบกับค่าจริง

ดังนั้นฉันคิดว่าแผนช้าเกี่ยวข้องกับเวอร์ชันที่กำหนดพารามิเตอร์ของแบบสอบถาม

ParameterCompiledValueเท่ากับParameterRuntimeValueทั้งหมดของพารามิเตอร์ดังนั้นนี้ไม่พารามิเตอร์ทั่วไปดม (ที่แผนถูกเรียบเรียงสำหรับหนึ่งชุดของค่าทำงานแล้วสำหรับชุดของค่าอื่น)

ปัญหาคือแผนการที่รวบรวมไว้สำหรับค่าพารามิเตอร์ที่ถูกต้องนั้นไม่เหมาะสม

คุณมีแนวโน้มที่จะตีปัญหากับจากน้อยไปมากวันที่อธิบายไว้ที่นี่และที่นี่ สำหรับตารางที่มี 100 ล้านแถวคุณต้องแทรก (หรือแก้ไขอย่างอื่น) 20 ล้านแถวก่อนที่ SQL Server จะอัปเดตสถิติให้คุณโดยอัตโนมัติ ดูเหมือนว่าครั้งล่าสุดที่พวกเขาได้รับการปรับปรุงแถวศูนย์ตรงกับช่วงวันที่ในแบบสอบถาม แต่ตอนนี้ 7 ล้านทำ

คุณสามารถกำหนดเวลาการอัปเดตสถิติบ่อยครั้งมากขึ้นพิจารณาการตั้งค่าสถานะการติดตาม2389 - 90หรือการใช้OPTIMIZE FOR UKNOWNดังนั้นเพียงแค่ย้อนกลับไปคาดเดาแทนที่จะสามารถใช้สถิติที่ทำให้เข้าใจผิดในปัจจุบันในdatetimeคอลัมน์

สิ่งนี้อาจไม่จำเป็นใน SQL Server เวอร์ชันถัดไป (หลังปี 2012) รายการ Connect ที่เกี่ยวข้องมีการตอบสนองที่น่าสนใจ

โพสต์โดย Microsoft เมื่อ 8/28/2012 เวลา 13:35 น.
เราได้ทำการปรับปรุงการประมาณค่า cardinality สำหรับการเปิดตัวครั้งสำคัญครั้งต่อไปที่จะแก้ไขสิ่งนี้ คอยติดตามรายละเอียดเมื่อภาพตัวอย่างของเราออกมา เอริค

การปรับปรุงในปี 2014 นี้ถูกมองโดย Benjamin Nevarez ในตอนท้ายของบทความ:

ดูครั้งแรกที่นิว SQL Server Cardinality ประมาณการ

ดูเหมือนว่าตัวประมาณค่า cardinality ใหม่จะถอยกลับและใช้ความหนาแน่นเฉลี่ยในกรณีนี้แทนที่จะให้ค่าประมาณ 1 แถว

รายละเอียดเพิ่มเติมเกี่ยวกับตัวประมาณค่า cardinality 2014 และปัญหาสำคัญจากที่นี่:

ฟังก์ชั่นใหม่ใน SQL Server 2014 - ส่วนที่ 2 - การประมาณค่าใหม่ของ Cardinality


29

ดังนั้นคำถามของฉันคือสิ่งนี้ ... พารามิเตอร์การดมกลิ่นสามารถตำหนิได้อย่างไรเมื่อเราได้รับแบบสอบถามช้าแบบเดียวกันในแคชแผนว่างเปล่า ... ไม่ควรมีพารามิเตอร์ใด ๆ ในการดมกลิ่น?

เมื่อ SQL Server รวบรวมแบบสอบถามที่มีค่าพารามิเตอร์จะดมกลิ่นค่าเฉพาะของพารามิเตอร์เหล่านั้นสำหรับการประเมิน cardinality (จำนวนแถว) ในกรณีของคุณโดยเฉพาะอย่างยิ่งค่าของ@BeginDate, @EndDateและ@ClientIDจะใช้เมื่อเลือกแผนประหาร คุณสามารถค้นหารายละเอียดเพิ่มเติมเกี่ยวกับพารามิเตอร์การดมกลิ่นที่นี่และที่นี่ ฉันกำลังให้การเชื่อมโยงพื้นหลังเหล่านี้เพราะคำถามข้างต้นทำให้ฉันคิดว่าแนวคิดนี้มีความเข้าใจที่ไม่สมบูรณ์ในปัจจุบัน - มีค่าพารามิเตอร์ที่จะดมกลิ่นเมื่อมีการรวบรวมแผน

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

แผนช้าดมกลิ่นค่า

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

การประมาณแถวเดี่ยวยังเป็นสาเหตุที่เครื่องมือเพิ่มประสิทธิภาพหยุดมองหาแผนการที่ดีกว่ากลับมาเป็นข้อความ Good Plan พอพบ ต้นทุนรวมโดยประมาณของแผนช้าด้วยการประมาณการแถวเดี่ยวเพียง 0.013136 หน่วยต้นทุนดังนั้นจึงไม่มีประเด็นที่จะพยายามค้นหาอะไรที่ดีกว่า ยกเว้นการค้นหาจริง ๆ แล้วจะส่งคืน 7,388,383 แถวแทนที่จะเป็นหนึ่งแถวทำให้มีการค้นหาคีย์ในจำนวนเดียวกัน

สถิติอาจเป็นเรื่องยุ่งยากในการติดตามและเป็นประโยชน์ในตารางขนาดใหญ่และการแบ่งพาร์ทิชันแนะนำความท้าทายของตนเองในเรื่องนั้น ฉันไม่ได้ประสบความสำเร็จโดยเฉพาะอย่างยิ่งตัวเองด้วยการติดตามค่าสถานะ 2389 และ 2390 แต่คุณสามารถทดสอบได้ บิวด์ล่าสุดของ SQL Server (R2 SP1 และใหม่กว่า) มีการอัพเดตสถิติแบบไดนามิกที่พร้อมใช้งาน แต่การอัพเดตสถิติสำหรับแต่ละพาร์ติชันยังไม่ได้นำมาใช้ ในระหว่างนี้คุณอาจต้องการกำหนดการอัปเดตสถิติด้วยตนเองทุกครั้งที่คุณทำการเปลี่ยนแปลงที่สำคัญในตารางนี้

สำหรับแบบสอบถามเฉพาะนี้ฉันจะคิดเกี่ยวกับการใช้ดัชนีที่แนะนำโดยเครื่องมือเพิ่มประสิทธิภาพในระหว่างการรวบรวมแผนแบบสอบถามอย่างรวดเร็ว:

/*
The Query Processor estimates that implementing the following index could improve
the query cost by 98.8091%.

WARNING: This is only an estimate, and the Query Processor is making this 
recommendation based solely upon analysis of this specific query.
It has not considered the resulting index size, or its workload-wide impact,
including its impact on INSERT, UPDATE, DELETE performance.
These factors should be taken into account before creating this index.
*/
CREATE NONCLUSTERED INDEX [<Name of Missing Index>]
ON [dbo].[PDetail] ([ClientID],[PostedDate])
INCLUDE ([Amount]);

ดัชนีควรจัดเรียงพาร์ติชันพร้อมกับON PartitionSchemeName (PostedDate)ประโยค แต่จุดคือการให้เส้นทางการเข้าถึงข้อมูลที่ดีที่สุดอย่างเห็นได้ชัดจะช่วยให้เครื่องมือเพิ่มประสิทธิภาพหลีกเลี่ยงตัวเลือกแผนไม่ดีโดยไม่ต้องใช้OPTIMIZE FOR UNKNOWNคำแนะนำหรือวิธีการแก้ปัญหาแบบเก่าเช่นการใช้ตัวแปรท้องถิ่น

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


ต้องการฉันสามารถทำเครื่องหมายสองคำตอบว่าถูกต้อง แต่อีกครั้งขอบคุณสำหรับข้อมูลเพิ่มเติม - ให้คำแนะนำมาก
RThomas

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

0

ฉันมีปัญหาเดียวกันที่ขั้นตอนการจัดเก็บช้าOPTIMIZE FOR UNKNOWNและRECOMPILEคำแนะนำการสืบค้นแก้ไขความช้าและเร่งเวลาดำเนินการ อย่างไรก็ตามสองวิธีต่อไปนี้ไม่ได้ส่งผลกระทบต่อความช้าของขั้นตอนการจัดเก็บ: (i) การล้างแคช (ii) โดยใช้ WITH RECOMPILE ดังนั้นอย่างที่คุณพูดมันไม่ใช่การดมกลิ่นพารามิเตอร์

การติดตามสถานะ 2389 และ 2390 ก็ไม่ได้ช่วยเช่นกัน แค่อัปเดตสถิติ ( EXEC sp_updatestats) ก็ทำได้สำหรับฉัน

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