OPTION (RECOMPILE) เร็วกว่าเสมอ ทำไม?


169

ฉันพบสถานการณ์แปลก ๆ ที่ผนวกOPTION (RECOMPILE)กับแบบสอบถามของฉันทำให้การทำงานในครึ่งวินาทีในขณะที่ละเว้นมันทำให้แบบสอบถามใช้เวลานานกว่าห้านาที

นี่คือกรณีที่เมื่อแบบสอบถามจะถูกดำเนินการจากการวิเคราะห์คำถามหรือจากโปรแกรมของฉัน C # SqlCommand.ExecuteReader()ผ่าน โทร (หรือไม่โทร) DBCC FREEPROCCACHEหรือDBCC dropcleanbuffersสร้างความแตกต่าง; ผลลัพธ์การสืบค้นจะส่งคืนทันทีด้วยOPTION (RECOMPILE)และมากกว่าห้านาทีโดยไม่มี แบบสอบถามจะถูกเรียกด้วยพารามิเตอร์เดียวกันเสมอ [เพื่อประโยชน์ในการทดสอบนี้]

ฉันใช้ SQL Server 2008

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

มันผิดปกติหรือไม่ที่มีคิวรีที่ต้องการคำแนะนำการคอมไพล์ซ้ำในการโทรทุกครั้ง?

ขออภัยสำหรับคำถามระดับเริ่มต้น แต่ฉันไม่สามารถสร้างหัวหรือก้อยของเรื่องนี้ได้

อัปเดต: ฉันถูกขอให้โพสต์ข้อความค้นหา ...

select acctNo,min(date) earliestDate 
from( 
    select acctNo,tradeDate as date 
    from datafeed_trans 
    where feedid=@feedID and feedDate=@feedDate 

    union 

    select acctNo,feedDate as date 
    from datafeed_money 
    where feedid=@feedID and feedDate=@feedDate 

    union 

    select acctNo,feedDate as date 
    from datafeed_jnl 
    where feedid=@feedID and feedDate=@feedDate 
)t1 
group by t1.acctNo
OPTION(RECOMPILE)

เมื่อรันการทดสอบจาก Query Analyzer ฉันจะเติมบรรทัดต่อไปนี้:

declare @feedID int
select @feedID=20

declare @feedDate datetime
select @feedDate='1/2/2009'

เมื่อเรียกมันจากโปรแกรม C # ของฉันพารามิเตอร์จะถูกส่งผ่านทางSqlCommand.Parametersคุณสมบัติ

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


3
พารามิเตอร์ของแบบสอบถามคืออะไร ตรวจสอบบทความนี้ออก blogs.msdn.com/b/turgays/archive/2013/09/10/… โดยทั่วไปแล้ว SQL จะพยายามสร้างแผนแบบสอบถามตามพารามิเตอร์เมื่อ proc รวบรวมเป็นครั้งแรก มันอาจสร้างแผนการที่ไม่เหมาะสมเมื่อคุณเริ่มผ่านพารามิเตอร์ที่แตกต่างกันและเป็นจริงมากขึ้น
Sparky

3
แบบสอบถามสั้นพอที่จะแสดงรายการที่นี่หรือไม่ ฉันคิดว่า Sparky นั้นถูกต้องและอาจเกี่ยวข้องกับการดมพารามิเตอร์ฉันมีปัญหาที่คล้ายกันซึ่งทำให้ฉันสับสนจนอ่านบทความที่ยอดเยี่ยมนี้: sommarskog.se/query-plan-mysteries.html
Chris

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

2
สิ่งนี้สามารถเกิดขึ้นได้เพราะมันดมค่าของพารามิเตอร์และตัวแปรหรือเพราะมันทำให้เข้าใจง่ายขึ้น ตัวอย่างของ simplifications มากขึ้นจะถูกยุบX = @X OR @X IS NULLไปX=@Xและดำเนินการขอความเห็นที่นี่หรือผลักดันภาคลงต่อกับมุมมองที่มีฟังก์ชั่นหน้าต่าง
มาร์ตินสมิ ธ

3
หลังการแก้ไขของคุณตัวอย่างเคียวรีตัววิเคราะห์จะใช้ตัวแปรไม่ใช่พารามิเตอร์ RECOMPILEค่าของคนเหล่านั้นไม่เคยดมยกเว้น ในทุกเหตุการณ์จับแผนการดำเนินการและดูความแตกต่าง
Martin Smith

คำตอบ:


157

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

EXEC sp_updatestats

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

การเพิ่มOPTION(RECOMPILE)แผนการดำเนินการสร้างใหม่ทุกครั้งที่แบบสอบถามของคุณดำเนินการ ฉันไม่เคยได้ยินคำอธิบายที่อธิบายcreates a new lookup strategyแต่บางทีเราอาจจะแค่ใช้คำศัพท์ต่างกันในสิ่งเดียวกัน

เมื่อมีการสร้างโพรซีเดอร์ที่เก็บไว้ (ฉันสงสัยว่าคุณกำลังเรียก ad-hoc sql จาก. NET แต่ถ้าคุณใช้เคียวรีแบบพารามิเตอร์แล้วนี่จะเป็นการโทร proc ที่จัดเก็บไว้ ) SQL Server จะพยายามกำหนดแผนการดำเนินการที่มีประสิทธิภาพที่สุดสำหรับเคียวรีนี้ ขึ้นอยู่กับข้อมูลในฐานข้อมูลของคุณและพารามิเตอร์ที่ส่งผ่าน (การดมกลิ่นพารามิเตอร์ ) จากนั้นแคชแผนนี้ ซึ่งหมายความว่าถ้าคุณสร้างแบบสอบถามที่มี 10 ระเบียนในฐานข้อมูลของคุณแล้วดำเนินการเมื่อมี 100,000,000 ระเบียนแผนการดำเนินการแคชอาจไม่มีประสิทธิภาพมากที่สุดอีกต่อไป

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

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


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

3
เป็นวิธีเดียวที่ฉันพบว่าทำงานกับตัวแปรตารางเนื่องจาก SQL คิดเสมอว่ามีหนึ่งแถวเดียวเมื่อมันมีหลายพันแถวมันจะกลายเป็นปัญหา
Alex Zhukovskiy

4
หนึ่งรายละเอียดที่น่าสนใจ: สถิติการปรับปรุงโดยปริยายเลิกแผนแคชไว้ทั้งหมดซึ่งใช้สถิติเหล่านี้ แต่ถ้าสถิติการเปลี่ยนแปลงจริงหลังจากการดำเนินการปรับปรุง ดังนั้นสำหรับตารางแบบอ่านอย่างเดียวที่เบ้อย่างมากดูเหมือนว่าOPTION (RECOMPILE)วิธีแก้ปัญหาที่ชัดเจนอาจเป็นทางออกเดียว
Groo

141

บ่อยครั้งที่มีความแตกต่างอย่างมากจากการเรียกใช้การเรียกใช้แบบสอบถามฉันพบว่ามันมักจะเป็นหนึ่งใน 5 ปัญหา

  1. สถิติ- สถิติล้าสมัย ฐานข้อมูลจะจัดเก็บสถิติเกี่ยวกับช่วงและการกระจายของประเภทค่าในคอลัมน์ต่างๆบนตารางและดัชนี สิ่งนี้จะช่วยให้เคียวรีเคียวรีพัฒนา "แผน" ของการโจมตีว่าจะทำเคียวรีอย่างไรเช่นชนิดของเมธอดที่จะใช้เพื่อจับคู่คีย์ระหว่างตารางโดยใช้แฮชหรือดูทั้งชุด คุณสามารถโทรอัพเดตสถิติบนฐานข้อมูลทั้งหมดหรือเพียงแค่ตารางหรือดัชนีบางอย่าง สิ่งนี้ทำให้การสืบค้นช้าลงจากการทำงานหนึ่งไปยังอีกการทำงานหนึ่งเนื่องจากเมื่อสถิติล้าสมัยไปแล้วน่าจะเป็นไปได้ว่าแผนแบบสอบถามไม่เหมาะสำหรับข้อมูลที่ถูกแทรกหรือเปลี่ยนแปลงใหม่สำหรับแบบสอบถามเดียวกัน (อธิบายเพิ่มเติมในภายหลังด้านล่าง) อาจไม่เหมาะสมในการอัปเดตสถิติในฐานข้อมูลการผลิตทันทีเนื่องจากจะมีค่าใช้จ่ายบางส่วนชะลอตัวและล่าช้าขึ้นอยู่กับปริมาณข้อมูลที่จะสุ่มตัวอย่าง คุณยังสามารถเลือกใช้การสแกนแบบเต็มหรือการสุ่มตัวอย่างเพื่ออัปเดตสถิติได้ หากคุณดูที่ Query Plan คุณสามารถดูสถิติของดัชนีที่ใช้งานโดยใช้คำสั่งDBCC SHOW_STATISTICS (tablename, indexname) สิ่งนี้จะแสดงการกระจายและช่วงของคีย์ต่างๆที่ใช้แผนคิวรีเพื่อยึดแนวทางของมัน

  2. การตั้งค่าพารามิเตอร์ - แผนแบบสอบถามที่แคชไม่เหมาะสำหรับพารามิเตอร์เฉพาะที่คุณส่งผ่านแม้ว่าตัวแบบสอบถามจะไม่เปลี่ยนแปลง ตัวอย่างเช่นหากคุณส่งผ่านพารามิเตอร์ที่ดึงเฉพาะ 10 จาก 1,000,000 แถวแผนแบบสอบถามที่สร้างขึ้นอาจใช้ Hash Join อย่างไรก็ตามหากพารามิเตอร์ที่คุณส่งผ่านจะใช้ 750,000 จาก 1,000,000 แถวแผนที่สร้างขึ้นอาจเป็น สแกนดัชนีหรือสแกนตาราง ในสถานการณ์เช่นนี้คุณสามารถบอกคำสั่ง SQL ให้ใช้ตัวเลือกOPTION (RECOMPILE)หรือ SP เพื่อใช้กับ RECOMPILE เพื่อบอกเครื่องยนต์นี่คือ "แผนการใช้ครั้งเดียว" และไม่ใช้แผนแคชซึ่งอาจไม่มีผลบังคับใช้ ไม่มีกฎในการตัดสินใจนี้ขึ้นอยู่กับการรู้วิธีที่ผู้ใช้จะใช้แบบสอบถาม

  3. INDEXES - เป็นไปได้ว่าแบบสอบถามไม่ได้เปลี่ยนแปลง แต่การเปลี่ยนแปลงที่อื่นเช่นการลบดัชนีที่มีประโยชน์มากทำให้การสืบค้นช้าลง

  4. เปลี่ยนแถวแล้ว - แถวที่คุณสอบถามมีการเปลี่ยนแปลงอย่างมากจากการโทรถึงโทร โดยปกติแล้วสถิติจะได้รับการอัปเดตโดยอัตโนมัติในกรณีเหล่านี้ อย่างไรก็ตามถ้าคุณกำลังสร้าง SQL แบบไดนามิกหรือการเรียก SQL ภายในวงแบบวนซ้ำมีความเป็นไปได้ที่คุณจะใช้ Query Plan ที่ล้าสมัยโดยพิจารณาจากจำนวนแถวหรือสถิติที่ไม่ถูกต้อง อีกครั้งในกรณีนี้OPTION (RECOMPILE)มีประโยชน์

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

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


2
ขอบคุณเพื่อน. นี่คือข้อมูลที่ยอดเยี่ยม ฉันจะไม่สามารถเข้าใจคำตอบของคุณเมื่อฉันโพสต์คำถามของฉัน แต่ตอนนี้มันเหมาะสมกับฉัน
Chad Decker

3
พารามิเตอร์ของการถ่ายภาพเป็นสิ่งที่เลวร้ายที่สุดในการดำรงอยู่ของฉัน ฉันไม่รู้ด้วยซ้ำเกี่ยวกับคำสั่งนี้จนกระทั่งคำถามสัมภาษณ์ล้มเหลว โซลูชันการดมกลิ่นพารามิเตอร์ของฉันมักจะแฮชค่าพารามิเตอร์และผนวก "AND {hash} = {hash}" เพื่อให้ sql แตกต่างกันเสมอสำหรับค่าที่แตกต่างกัน แฮ็ค แต่มันใช้งานได้
Jeremy Boyd

27

เพื่อเพิ่มไปยังรายการที่ยอดเยี่ยม (กำหนดโดย @CodeCowboyOrg) ของสถานการณ์ที่ตัวเลือก (RECOMPILE) มีประโยชน์มาก

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

1
ฉันมีคิวรีพร้อมตัวแปรตาราง 5 ตัว บนเครื่องของฉันมันทำงานได้นานกว่าครึ่งชั่วโมง บนเครื่องเพื่อนร่วมงานของฉันมันรันใน <1 วินาที เครื่องมีฮาร์ดแวร์ที่คล้ายกันและรุ่น SQL Server เดียวกัน ถ้าเราทั้งสองเพิ่ม OPTION (RECOMPILE) มันจะทำงานใน 2 วินาทีบนเครื่องทั้งสอง ในทุกกรณีการทดสอบการดำเนินการจะดำเนินการใน SSMS สิ่งที่อาจทำให้เกิดความแตกต่างนี้
Adam

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

1
สำหรับตารางชั่วคราวมันเป็นสถานการณ์เดียวกันหรือไม่?
Muflix

1
@muflix: คำถามที่ดี ฉันไม่เชื่อว่าผลจะเหมือนกันสำหรับตารางชั่วคราวเนื่องจากพวกเขามีสถิติและเอ็นจิ้นควรเลือกตัวเลือกการคอมไพล์แบบอัตโนมัติเช่นเดียวกับตารางอื่นฉันเชื่อว่า (แต่ไม่แน่ใจ) บางทีคนอื่นรู้ด้วยความมั่นใจมากขึ้น
DWright

2
สถิติในตารางชั่วคราวจะไม่อัปเดตหรือคอมไพล์ใหม่โดยอัตโนมัติดังนั้นโปรแกรมเมอร์ต้องทำ
J. Michael Wuerth

1

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

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

เป็นตัวอย่าง: สร้างดัชนี idx01_datafeed_trans บน datafeed_trans (feedid, feedDate) INCLUDE (acctNo, tradeDate)

หากแผนมีความเสถียรหรือคุณสามารถทำให้มีเสถียรภาพคุณสามารถรันประโยคด้วย sp_executesql ('ประโยค sql') เพื่อบันทึกและใช้แผนการดำเนินการคงที่

หากแผนไม่เสถียรคุณต้องใช้คำสั่ง ad-hoc หรือ EXEC ('ประโยค sql') เพื่อประเมินและสร้างแผนการดำเนินการในแต่ละครั้ง (หรือโพรซีเดอร์ที่เก็บไว้ "with recompile")

หวังว่ามันจะช่วย


1

ลบล้างคำถามนี้ แต่มีคำอธิบายว่าไม่มีใครพิจารณา

สถิติ - สถิติไม่พร้อมใช้งานหรือทำให้เข้าใจผิด

หากทั้งหมดต่อไปนี้เป็นจริง:

  1. คอลัมน์ feedid และ feedDate มีแนวโน้มที่จะมีความสัมพันธ์สูง (เช่น id ฟีดนั้นมีความเฉพาะเจาะจงมากกว่าวันที่ฟีดและพารามิเตอร์ date เป็นข้อมูลที่ซ้ำซ้อน)
  2. ไม่มีดัชนีที่มีทั้งสองคอลัมน์เป็นคอลัมน์เรียงตามลำดับ
  3. ไม่มีสถิติที่สร้างด้วยตนเองซึ่งครอบคลุมทั้งสองคอลัมน์เหล่านี้

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

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