คำสั่ง SQL Server ช้าเป็นระยะ ๆ บน SQL Server 2008 R2


13

ในลูกค้ารายหนึ่งของเราเราพบปัญหาเกี่ยวกับประสิทธิภาพในการสมัครของเรา เป็นแอปพลิเคชั่นเว็บ. NET 3.5 ที่ใช้งานและอัพเดทข้อมูลในฐานข้อมูล SQL Server ปัจจุบันสภาพแวดล้อมการผลิตของเราประกอบด้วยเครื่อง Windows 2008 R2 เป็นส่วนหน้าและคลัสเตอร์ SQL Server 2008 R2 ที่ส่วนหลัง แอพของเราใช้ COM + และ MSDTC เพื่อเชื่อมต่อกับฐานข้อมูล

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

ปัญหาคือเมื่อฉันเรียกใช้ขั้นตอนการจัดเก็บเหล่านี้โดยตรงในฐานข้อมูลใน SQL Management Studio บางครั้งพวกเขาใช้เวลานาน (ประมาณ 7/8 วินาที) บางครั้งก็รวดเร็ว (น้อยกว่า 1 วินาที) ฉันไม่รู้ว่าทำไมสิ่งนี้ถึงเกิดขึ้นและมันทำให้ฉันสับสนเพราะเครื่อง SQL (4 คอร์, 32 GB) ไม่ได้ถูกใช้โดยแอพพลิเคชั่นอื่นและแบบสอบถามเหล่านี้ไม่ควรใช้เวลานานในการรัน

ไม่ใช่การเป็น DBA หรือกูรูเซิร์ฟเวอร์ SQL ฉันพยายามค้นหาสิ่งที่อาจช่วยให้ฉันเข้าใจปัญหา นี่คือขั้นตอนที่ฉันพยายามทำและแยกแยะปัญหาและสิ่งที่ฉันค้นพบ:

  • รหัส TSQL ทั้งหมดที่เรียกใช้โดยแอปพลิเคชันจะถูกเขียนในขั้นตอนการจัดเก็บ
  • ฉันระบุข้อความค้นหาที่ใช้เวลานานใน SQL Server Profiler แต่เมื่อฉันเรียกใช้งานเหล่านี้ใน Management Studio พวกเขาอาจใช้เวลานานในการรัน (จาก 4 ถึง 10 วินาที) หรือทำงานอย่างรวดเร็ว (ต่ำกว่า 1 วินาที) ฉันกำลังเรียกใช้คำค้นหาเดียวกันที่แน่นอนด้วยข้อมูลเดียวกันที่ส่งผ่านในพารามิเตอร์ แบบสอบถามเหล่านี้ส่วนใหญ่จะถูกจัดเก็บด้วยวิธีการเลือกคำสั่งในพวกเขา
  • ฉันลองดูสถิติการรอและรอเพื่อลองและคิดออกว่ามีกระบวนการรอทรัพยากรบางอย่างอยู่หรือไม่ ฉันเรียกใช้แบบสอบถามต่อไปนี้:

WITH Waits AS
    (SELECT
        wait_type,
        wait_time_ms / 1000.0 AS WaitS,
        (wait_time_ms - signal_wait_time_ms) / 1000.0 AS ResourceS,
        signal_wait_time_ms / 1000.0 AS SignalS,
        waiting_tasks_count AS WaitCount,
        100.0 * wait_time_ms / SUM (wait_time_ms) OVER() AS Percentage,
        ROW_NUMBER() OVER(ORDER BY wait_time_ms DESC) AS RowNum
    FROM sys.dm_os_wait_stats
    WHERE wait_type NOT IN (
        'CLR_SEMAPHORE', 'LAZYWRITER_SLEEP', 'RESOURCE_QUEUE', 'SLEEP_TASK',
        'SLEEP_SYSTEMTASK', 'SQLTRACE_BUFFER_FLUSH', 'WAITFOR', 'LOGMGR_QUEUE',
        'CHECKPOINT_QUEUE', 'REQUEST_FOR_DEADLOCK_SEARCH', 'XE_TIMER_EVENT',  'BROKER_TO_FLUSH',
        'BROKER_TASK_STOP', 'CLR_MANUAL_EVENT', 'CLR_AUTO_EVENT',     'DISPATCHER_QUEUE_SEMAPHORE',
        'FT_IFTS_SCHEDULER_IDLE_WAIT', 'XE_DISPATCHER_WAIT', 'XE_DISPATCHER_JOIN', 'BROKER_EVENTHANDLER',
        'TRACEWRITE', 'FT_IFTSHC_MUTEX', 'SQLTRACE_INCREMENTAL_FLUSH_SLEEP',
        'BROKER_RECEIVE_WAITFOR', 'ONDEMAND_TASK_QUEUE', 'DBMIRROR_EVENTS_QUEUE',
        'DBMIRRORING_CMD', 'BROKER_TRANSMITTER', 'SQLTRACE_WAIT_ENTRIES',
        'SLEEP_BPOOL_FLUSH', 'SQLTRACE_LOCK')
    )
SELECT
    W1.wait_type AS WaitType, 
    CAST (W1.WaitS AS DECIMAL(14, 2)) AS Wait_S,
    CAST (W1.ResourceS AS DECIMAL(14, 2)) AS Resource_S,
    CAST (W1.SignalS AS DECIMAL(14, 2)) AS Signal_S,
    W1.WaitCount AS WaitCount,
    CAST (W1.Percentage AS DECIMAL(4, 2)) AS Percentage,
    CAST ((W1.WaitS / W1.WaitCount) AS DECIMAL (14, 4)) AS AvgWait_S,
    CAST ((W1.ResourceS / W1.WaitCount) AS DECIMAL (14, 4)) AS AvgRes_S,
    CAST ((W1.SignalS / W1.WaitCount) AS DECIMAL (14, 4)) AS AvgSig_S
FROM Waits AS W1
    INNER JOIN Waits AS W2 ON W2.RowNum <= W1.RowNum
GROUP BY W1.RowNum, W1.wait_type, W1.WaitS, W1.ResourceS, W1.SignalS, W1.WaitCount,    W1.Percentage
HAVING SUM (W2.Percentage) - W1.Percentage < 95; -- percentage threshold
GO

นี่คือสิ่งที่ฉันค้นพบ:

  • หลังจากฉันรีเซ็ตสถิติโดยใช้ DBCC SQLPERF (ประมาณ 1 หรือ 2 ชั่วโมงหลังจากนั้น) ประเภทการรอที่ฉันมีมากที่สุดคือ SOS_SCHEDULER_YIELD และ WRITELOG
  • เมื่อเวลาผ่านไป (หลังจากดำเนินการประมาณ 1 วัน) ประเภทการรอที่เกิดขึ้นมากที่สุดในฐานข้อมูลคือ CXPACKET (67%) และ OLEDB (17%) ถึงแม้ว่าเวลารอเฉลี่ยของแต่ละคนจะไม่นาน ฉันยังสังเกตเห็นว่าคำสั่งที่รันนานกว่าที่ระบุบน SQL Profiler เป็นการเรียกโพรซีเดอร์ที่เก็บไว้ซึ่งส่งคืนมากกว่าหนึ่ง resultset (มักจะ 3) มีปัญหาเกี่ยวกับอัมพาตที่นี่ได้ไหม? มีวิธีใดที่ฉันสามารถลองระบุได้ว่าเป็นสาเหตุของปัญหาหรือไม่
  • ฉันได้อ่านบางที่ OLEDB รออาจเกิดจากการโทรไปยังทรัพยากร OLEDB เช่นเซิร์ฟเวอร์ที่เชื่อมโยง เรามีเซิร์ฟเวอร์ที่เชื่อมโยงเพื่อเชื่อมต่อกับเครื่องทำดัชนีบริการ (MSIDXS) อย่างไรก็ตามไม่มีคำสั่งใดที่ระบุว่าใช้งานได้นานใช้ประโยชน์จากเซิร์ฟเวอร์ที่เชื่อมโยงนั้น
  • เวลารอเฉลี่ยที่สูงขึ้นที่ฉันมีสำหรับประเภทรอ LCK_M_X (เฉลี่ย 1.5 วินาที) แต่การรอประเภทนี้ไม่เกิดขึ้นบ่อยนักเมื่อเทียบกับประเภทอื่น ๆ (ตัวอย่างเช่น 64 LCK_M_X รอ vs 10,823 CXPACKET รอในช่วงเวลาเดียวกัน )
  • สิ่งหนึ่งที่ฉันสังเกตเห็นคือบริการ MSDTC ไม่ได้เป็นกลุ่ม บริการ SQL Server ถูกทำคลัสเตอร์ แต่ไม่ใช่ MSDTC จะมีการเข้าชมการทำงานเนื่องจากเหตุนี้หรือไม่ เรากำลังใช้ MSDTC เพราะแอปของเราใช้ Enterprise Services (DCOM) เพื่อเข้าถึงฐานข้อมูล แต่เซิร์ฟเวอร์ไม่ได้ติดตั้งและกำหนดค่าโดยเรา แต่ลูกค้าของเรา

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

คำตอบ:


4

ขอบคุณสำหรับคำอธิบายโดยละเอียดเกี่ยวกับปัญหาของคุณ (หนึ่งในคำถามที่ถูกจัดวางอย่างดีที่สุด)

WRITELOG เป็นรูปแบบการรอคอยทั่วไปจึงไม่ต้องกังวล เมื่อดูที่ SOS_SCHEDULER_YIELD บ่งบอกถึงแรงกดดันของ CPU และ CXPACKET เป็นไปได้ว่าต้องมีดัชนีหายไปและคุณอาจดึงข้อมูลจำนวนมากจากการสืบค้นสำหรับระบบ OLTP ฉันแนะนำให้คุณดู DMV ที่ขาดหายไปและดูว่ามีดัชนีใด ๆ (เกือบจะแน่ใจว่าจะมีมากกว่าสองสาม) ที่อยู่ใน procs ที่น่าสงสัย

http://sqlfool.com/2009/04/a-look-at-missing-indexes/

http://troubleshootingsql.com/2009/12/30/how-to-find-out-the-missing-indexes-on-a-sql-server-2008-or-2005-instance-along-with-the- สร้างดัชนีคำสั่ง /

มองหาโพสต์ของ Jonathan Kehayias บน sqlblog.com ด้วย

นอกจากนี้ลองดูพารามิเตอร์การดมกลิ่น

http://sommarskog.se/query-plan-mysteries.html

http://pratchev.blogspot.com/2007/08/parameter-sniffing.html

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


1

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

ตัวอย่างเช่น (ลดความซับซ้อนของหลักสูตร):

หากโมเดลเป็น "X" ประโยคที่ค้นหาProductCode จะเท่ากับค่าที่แน่นอน
ถ้าModelเป็น "Y" ประโยคที่ค้นหาProductType จะเท่ากับค่าที่แน่นอน

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

คุณสามารถลองวาง " With RECOMPILE " ที่ด้านบนของขั้นตอนการจัดเก็บ สร้างขั้นตอน (Transact-SQL)

วิธีที่ดีที่สุดที่ฉันสามารถอธิบายได้มีดังนี้:

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


การใช้exec()ฟังก์ชั่นจะอธิบายพฤติกรรมที่สังเกตได้ ในกรณีนี้การใช้งานsp_executesqlตามปกติจะช่วยแก้ไขปัญหาเกี่ยวกับคำสั่ง SQL แบบไดนามิก
ajeh

1

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

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

จำนวนแถวโดยประมาณในแผนการดำเนินการเมื่อเทียบกับจำนวนแถวจริงที่ถูกส่งคืน

พารามิเตอร์ที่คอมไพล์ตรงกับพารามิเตอร์เคียวรีจริงหรือไม่?

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

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

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

หากการสร้างสถิติของคุณไม่สามารถแก้ปัญหาได้คุณสามารถเรียกใช้คำสั่งที่แพงที่สุดในขั้นตอนการจัดเก็บด้วย OPTION (RECOMPILE)


0

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

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