วิธีวัดหรือค้นหาค่าใช้จ่ายในการสร้างแผนแบบสอบถาม


18

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

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

จะทราบได้อย่างไรว่าต้นทุนการสร้างแผนแบบสอบถามเป็นอย่างไร

เพื่อตอบคำถามของฉันฉันได้ Googled (เช่นกับการค้นหานี้ ) และฉันได้อ่านเอกสารของคอลัมน์สำหรับ dm_exec_query_statsDMVแล้ว ฉันได้ตรวจสอบหน้าต่างผลลัพธ์ใน SSMS สำหรับ "Actual Query Plan" เพื่อค้นหาข้อมูลนี้ สุดท้ายผมได้DBA.SE สืบค้น ไม่มีคำตอบใดที่นำไปสู่

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


5
ฉันจะแนะนำโลภสำเนาของภายในคำถามของ SQL Server เพิ่มประสิทธิภาพโดยเบนจามิน Nevarez แจกฟรี. บทที่ 5 'กระบวนการปรับให้เหมาะสม' อาจช่วยคุณคำนวณเวลารวบรวมสำหรับคิวรีของคุณ อย่างน้อยที่สุดมันเป็นข้อมูลเกี่ยวกับสิ่งที่เครื่องมือเพิ่มประสิทธิภาพดำเนินการเพื่อสร้างแผนคิวรี
Mark Sinkinson

คำตอบ:


18

จะทราบได้อย่างไรว่าต้นทุนในการสร้างแผนแบบสอบถามเป็นอย่างไร

คุณสามารถดูคุณสมบัติของโหนดรูทในแผนเคียวรีตัวอย่างเช่น:

สารสกัดจากคุณสมบัติของราก
(สกรีนช็อตจากSentry One Plan Explorer ฟรี )

ข้อมูลนี้ยังมีอยู่โดยการสอบถามแคชแผนตัวอย่างเช่นใช้แบบสอบถามตามความสัมพันธ์ต่อไปนี้:

WITH XMLNAMESPACES (DEFAULT 'http://schemas.microsoft.com/sqlserver/2004/07/showplan')
SELECT 
    CompileTime = c.value('(QueryPlan/@CompileTime)[1]', 'int'),
    CompileCPU = c.value('(QueryPlan/@CompileCPU)[1]', 'int'),
    CompileMemory = c.value('(QueryPlan/@CompileMemory)[1]', 'int'),
    ST.[text],
    QP.query_plan
FROM sys.dm_exec_cached_plans AS CP
CROSS APPLY sys.dm_exec_query_plan(CP.plan_handle) AS QP
CROSS APPLY sys.dm_exec_sql_text(CP.plan_handle) AS ST
CROSS APPLY QP.query_plan.nodes('ShowPlanXML/BatchSequence/Batch/Statements/StmtSimple') AS N(c);

ส่วนของผลลัพธ์

สำหรับการรักษาเต็มของตัวเลือกที่คุณมีในการจัดการทุกประเภทการค้นหาเหล่านี้ดู Erland Sommarskog ที่เพิ่งปรับปรุงบทความ


4

สมมติว่า "ต้นทุน" เป็นแง่ของเวลา (แม้ว่าไม่แน่ใจว่ามันจะเป็นอะไรในแง่ของ ;-) จากนั้นอย่างน้อยที่สุดคุณควรจะเข้าใจมันด้วยการทำสิ่งต่อไปนี้:

DBCC FREEPROCCACHE WITH NO_INFOMSGS;

SET STATISTICS TIME ON;

EXEC sp_help 'sys.databases'; -- replace with your proc

SET STATISTICS TIME OFF;

รายการแรกที่รายงานในแท็บ "ข้อความ" ควรเป็น:

SQL Server แยกวิเคราะห์และรวบรวมเวลา:

ฉันจะเรียกใช้อย่างน้อย 10 ครั้งและโดยเฉลี่ยทั้ง "CPU" และ "Elapsed" มิลลิวินาที

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

DECLARE @SQL NVARCHAR(MAX) = '';
;WITH cte AS
(
  SELECT DISTINCT stat.plan_handle
  FROM sys.dm_exec_query_stats stat
  CROSS APPLY sys.dm_exec_text_query_plan(stat.plan_handle, 0, -1) qplan
  WHERE qplan.query_plan LIKE N'%sp[_]help%' -- replace "sp[_]help" with proc name
)
SELECT @SQL += N'DBCC FREEPROCCACHE ('
               + CONVERT(NVARCHAR(130), cte.plan_handle, 1)
               + N');'
               + NCHAR(13) + NCHAR(10)
FROM cte;
PRINT @SQL;
EXEC (@SQL);

SET STATISTICS TIME ON;

EXEC sp_help 'sys.databases' -- replace with your proc

SET STATISTICS TIME OFF;

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

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

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

  • OPTION(OPTIMIZE FOR (@Param = value)) จะสร้างแผนตามค่าเฉพาะนั้นซึ่งอาจช่วยได้หลายกรณี แต่ยังคงทำให้คุณเปิดรับปัญหาปัจจุบัน

  • OPTION(OPTIMIZE FOR UNKNOWN)จะสร้างแผนตามจำนวนเงินที่จ่ายให้กับการแจกแจงเฉลี่ยซึ่งจะช่วยให้แบบสอบถามบางอย่าง แต่ทำอันตรายผู้อื่น นี่ควรเป็นตัวเลือกเดียวกับการใช้ตัวแปรโลคอล

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

  • จำเป็นต้องตรวจสอบความถูกต้องของพารามิเตอร์สตริงเพื่อป้องกัน SQL Injections

  • อาจจำเป็นต้องตั้งค่าผู้ใช้ที่ใช้ใบรับรองและใบรับรองเพื่อรักษาความปลอดภัยที่เป็นนามธรรมเนื่องจาก Dynamic SQL ต้องการการอนุญาตตารางโดยตรง

ดังนั้นนี่คือวิธีที่ฉันจัดการสถานการณ์นี้เมื่อฉันมี procs ที่ถูกเรียกมากกว่าหนึ่งครั้งต่อวินาทีและกดหลายตารางแต่ละแถวมีล้านแถว ฉันพยายามOPTION(RECOMPILE)แต่สิ่งนี้พิสูจน์แล้วว่าเป็นอันตรายต่อกระบวนการใน 99% ของกรณีที่ไม่มีปัญหาในการดมกลิ่นพารามิเตอร์ / แผนการแคชที่ไม่ดี และโปรดทราบว่าหนึ่งใน procs เหล่านี้มีคำค้นหาประมาณ 15 คำและมีเพียง 3 - 5 คนเท่านั้นที่ถูกแปลงเป็น Dynamic SQL ดังที่อธิบายไว้ที่นี่ Dynamic SQL ไม่ได้ถูกใช้ยกเว้นว่าจำเป็นสำหรับการสืบค้นเฉพาะ

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

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

  3. สำหรับพารามิเตอร์ที่เหลือที่เกี่ยวข้องกับการแจกแจงที่แตกต่างกันอย่างมากพารามิเตอร์เหล่านั้นควรถูกรวมเข้ากับ Dynamic SQL เป็นค่าตามตัวอักษร ตั้งแต่แบบสอบถามที่ไม่ซ้ำกันจะถูกกำหนดโดยการเปลี่ยนแปลงใด ๆ ข้อความแบบสอบถามที่มีเป็นแบบสอบถามที่แตกต่างกันและด้วยเหตุนี้แผนแบบสอบถามที่แตกต่างกันกว่ามีWHERE StatusID = 1WHERE StatusID = 2

  4. หากพารามิเตอร์อินพุต proc ใด ๆ ที่จะต่อกันเป็นข้อความของเคียวรีนั้นเป็นสตริงดังนั้นพวกเขาจำเป็นต้องตรวจสอบความถูกต้องเพื่อป้องกัน SQL Injection (แม้ว่าจะมีโอกาสน้อยกว่าที่จะเกิดขึ้นหากสตริงที่ส่งผ่านถูกสร้างขึ้นโดย แอปและไม่ใช่ผู้ใช้ แต่ยังคงมี) อย่างน้อยทำREPLACE(@Param, '''', '''''')เพื่อให้มั่นใจว่าคำพูดเดี่ยวจะกลายเป็นคำพูดเดียวที่หลบหนี

  5. หากจำเป็นให้สร้างใบรับรองที่จะใช้ในการสร้างผู้ใช้และลงนามในขั้นตอนการจัดเก็บเช่นการอนุญาตตารางโดยตรงจะได้รับอนุญาตเฉพาะกับผู้ใช้ที่ใช้ใบรับรองใหม่เท่านั้นและไม่ให้[public]หรือผู้ใช้ที่ไม่ควรมีสิทธิ์ดังกล่าว .

ตัวอย่าง proc:

CREATE PROCEDURE MySchema.MyProc
(
  @Param1 INT,
  @Param2 DATETIME,
  @Param3 NVARCHAR(50)
)
AS
SET NOCOUNT ON;

DECLARE @SQL NVARCHAR(MAX);

SET @SQL = N'
     SELECT  tab.Field1, tab.Field2, ...
     FROM    MySchema.SomeTable tab
     WHERE   tab.Field3 = @P1
     AND     tab.Field8 >= CONVERT(DATETIME, ''' +
  CONVERT(NVARCHAR(50), @Param2, 121) +
  N''')
     AND     tab.Field2 LIKE N''' +
  REPLACE(@Param3, N'''', N'''''') +
  N'%'';';

EXEC sp_executesql
     @SQL,
     N'@P1 INT',
     @P1 = @Param1;

ขอบคุณที่สละเวลาตอบ (ค่อนข้างบาง)! ฉันบิต แต่สงสัยเกี่ยวกับบิตแรกเกี่ยวกับวิธีการที่จะได้รับรวบรวมเวลาที่ได้รับว่ามันเป็นปัจจัยที่3ต่ำกว่าผลที่ฉันได้รับโดยใช้@ PaulWhite ของวิธีการ - ที่สองใน Dynamic SQL บิตเป็นสิ่งที่น่าสนใจ (แม้ว่ามันจะต้องใช้เวลาในการดำเนินการ; อย่างน้อยก็แค่ตบOPTIONแบบสอบถามของฉัน) และจะไม่ทำร้ายฉันมากเกินไปเนื่องจาก sproc นี้ถูกควบคุมอย่างดีในการทดสอบการรวม - ในกรณีใด ๆ : ขอบคุณสำหรับข้อมูลเชิงลึกของคุณ!
Jeroen
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.