ขั้นตอนที่จัดเก็บช้าเมื่อเรียกจากเว็บอย่างรวดเร็วจาก Management Studio


97

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

ฉันเริ่มใช้ Sql Profiler และติดตามการโทรที่หมดเวลาและในที่สุดก็พบสิ่งเหล่านี้:

  1. เมื่อดำเนินการคำสั่งจากภายใน MS SQL Management Studio โดยมีอาร์กิวเมนต์เดียวกัน (อันที่จริงฉันคัดลอกการเรียกโพรซีเดอร์จากการติดตามโปรไฟล์ sql และรัน): มันจะเสร็จสิ้นใน 5 ~ 6 วินาทีโดยเฉลี่ย
  2. แต่เมื่อเรียกจากเว็บแอปพลิเคชันจะใช้เวลานานกว่า 30 วินาที (ตามรอย) ดังนั้นหน้าเว็บของฉันจึงหมดเวลาจริง

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

ฉันจะทราบได้อย่างไรว่าเกิดอะไรขึ้น

ฉันคิดว่ามันไม่มีส่วนเกี่ยวข้องกับการที่เราใช้เลเยอร์ BLL> DAL หรืออะแดปเตอร์ตารางเนื่องจากการติดตามแสดงให้เห็นอย่างชัดเจนถึงความล่าช้าในขั้นตอนจริง นั่นคือทั้งหมดที่ฉันคิดได้

แก้ไขฉันพบในลิงค์นี้ว่า ADO.NET ตั้งค่าARITHABORTเป็นจริงซึ่งเป็นสิ่งที่ดีสำหรับเกือบตลอดเวลา แต่บางครั้งสิ่งนี้ก็เกิดขึ้นและวิธีแก้ปัญหาที่แนะนำคือการเพิ่มwith recompileตัวเลือกให้กับ proc ที่เก็บไว้ ในกรณีของฉันมันใช้ไม่ได้ แต่ฉันสงสัยว่ามันคล้ายกับสิ่งนี้มาก มีใครรู้บ้างว่า ADO.NET ทำอะไรอีกหรือหา spec ได้ที่ไหน


1
สิ่งนี้อาจเกี่ยวข้องกับจำนวนข้อมูลที่ถูกส่งกลับ?
Barry Kaye

@Barry: ไม่เนื่องจากฉันเรียกใช้โพรซีเดอร์เดียวกัน (คัดลอกจากการติดตามด้วย - หมายถึงพารามิเตอร์เดียวกัน) ในสตูดิโอการจัดการจะทำงานภายใน 6 วินาที
iamserious

@Jayantha: ประเด็นไม่ได้อยู่ที่ sp ช้า แต่สิ่งที่อยู่ระหว่าง adm.net และ sql คือ ฉันไม่เห็นว่า sp จะสร้างความแตกต่างได้อย่างไร
iamserious

2
SP ส่งคืนข้อมูลจำนวนมากเช่นคอลัมน์รูปภาพ / ข้อความ / varchar (สูงสุด) หรือไม่ ปริมาณข้อมูลที่จะใช้กับไคลเอนต์จะมากซึ่งจะใช้เวลามาก SSMS ตัดผลลัพธ์เหล่านี้ออกไปในเรื่องที่มีประสิทธิภาพมากขึ้น
Tim Schmelter

1
อาจซ้ำกันได้ของstackoverflow.com/questions/2248112/…
Aaron Bertrand

คำตอบ:


79

ฉันเคยมีปัญหาคล้ายกันนี้เกิดขึ้นในอดีตดังนั้นฉันจึงอยากเห็นการแก้ปัญหาสำหรับคำถามนี้ ความคิดเห็นของ Aaron Bertrand เกี่ยวกับ OP ทำให้Query หมดเวลาเมื่อเรียกใช้งานจากเว็บ แต่จะเร็วมากเมื่อดำเนินการจาก SSMSและแม้ว่าคำถามจะไม่ซ้ำกันคำตอบอาจใช้ได้กับสถานการณ์ของคุณ

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

ดูADO.NET ที่เรียก T-SQL Stored Procedure ทำให้เกิด SqlTimeoutExceptionสำหรับตัวอย่างอื่นพร้อมคำอธิบายและความละเอียดที่สมบูรณ์ยิ่งขึ้น


นี่คือโอกาสในการขายที่น่าสนใจจะอ่านเพิ่มเติมเกี่ยวกับพวกเขาและจะกลับมาอีกในไม่กี่ .. ขอบคุณ
iamserious

46
สวัสดีการล้างแคชDBCC DROPCLEANBUFFERSและDBCC FREEPROCCACHEทำตามเคล็ดลับ! ฉันเดาว่าแผนการดำเนินการเสียหายหรือไม่ได้รับการอัปเดต ฉันสงสัยว่ามันเป็นการแก้ไขแบบถาวรถ้ามันเสียหายได้ครั้งเดียวก็สามารถทำได้อีกครั้ง ดังนั้นฉันยังคงมองหาสาเหตุที่เป็นไปได้ เนื่องจากเว็บแอปกลับมาทำงานอีกครั้งฉันจึงไม่จำเป็นต้องรู้สึกกดดัน ขอบคุณมาก.
iamserious

2
@iamserious - คุณตอกมันขอบคุณ! หลังจากแก้ไขขั้นตอนการจัดเก็บของฉันฉันมีปัญหาการหมดเวลา จากนั้นฉันเรียกใช้คำสั่ง DBCC สองคำสั่งที่คุณกล่าวถึงข้างต้นและแก้ไขปัญหาได้
Induster

5
@Mangist: เนื่องจากปัญหานี้เป็นปัญหาที่ซับซ้อนจึงไม่มีสัญลักษณ์แสดงหัวข้อย่อยสีเงิน แต่คุณสามารถลองใช้OPTIMIZE FORหรือOPTION(Recompile)สอบถามคำแนะนำเกี่ยวกับแบบสอบถามที่ทำให้เกิดปัญหาได้ บทความนี้กล่าวถึงปัญหาในเชิงลึก ถ้า Entity Framework กำลังสร้างการสืบค้นของคุณโพสต์นี้จะแสดงวิธีเพิ่มคำใบ้แบบสอบถามให้กับการสืบค้นของคุณ
StriplingWarrior

8
แทนที่จะเรียกใช้ DBCC DROPCLEANBUFFERS และ DBCC FREEPROCCACHE ซึ่งจะล้างแผนการดำเนินการทั้งหมดคุณสามารถวางและสร้างขั้นตอนที่จัดเก็บปัญหาขึ้นใหม่ได้
jnoreiga

52

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

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

เช่น. เปลี่ยนแปลง:

ALTER PROCEDURE [dbo].[sproc] 
    @param1 int,
AS
SELECT * FROM Table WHERE ID = @param1 

ถึง:

ALTER PROCEDURE [dbo].[sproc] 
    @param1 int,
AS
DECLARE @param1a int
SET @param1a = @param1
SELECT * FROM Table WHERE ID = @param1a

ดูเหมือนจะแปลก แต่มันช่วยแก้ปัญหาของฉันได้


3
ว้าวฉันมีปัญหาเดียวกันและไม่เชื่อว่าจะได้ผล แต่ก็ทำได้
Lac Ho

1
Zane คุณรู้ไหมว่าสิ่งนี้จะแก้ไขปัญหาได้อย่างถาวรหรือสามารถกลับมาได้? ขอบคุณ!
Lac Ho

1
ตั้งแต่ทำการเปลี่ยนแปลงนี้ฉันไม่มีปัญหาใด ๆ กับความเร็วของขั้นตอนการจัดเก็บ
Zane

1
ว้าวมันแก้ปัญหาของฉันด้วย! สิ่งนี้จะเป็นจุดบกพร่องในเซิร์ฟเวอร์ sql เหตุใดจึงต้องบังคับให้นักเขียน sql ข้ามผ่านห่วงโง่ ๆ นี้เมื่อ MSSQL สามารถแปลงเป็น local ภายใต้ประทุนเพื่อเพิ่มประสิทธิภาพการทำงานได้มาก
HerrimanCoder

3
เป็นไปได้ไหมว่าการเปลี่ยนแปลง proc ทำให้เกิดแผนการดำเนินการใหม่ดังนั้นโซลูชันจึงไม่ได้คัดลอกตัวแปรอินพุตไปยังตัวแปรภายใน แต่เพียงแค่บังคับให้สร้างแผนการดำเนินการใหม่ (ตามที่อธิบายไว้ในโซลูชันที่ยอมรับ)
Garland Pope

10

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

ฉันพยายามตั้งค่าขั้นตอนการจัดเก็บให้คอมไพล์ใหม่โดยใช้sp_recompileและสิ่งนี้แก้ไขปัญหาสำหรับ SP หนึ่ง

ในที่สุดก็มี SP จำนวนมากขึ้นที่หมดเวลาซึ่งส่วนใหญ่ไม่เคยทำมาก่อนโดยการใช้DBCC DROPCLEANBUFFERSและDBBC FREEPROCCACHEอัตราการหมดเวลาของเหตุการณ์ลดลงอย่างมีนัยสำคัญ - ยังคงมีเหตุการณ์ที่แยกออกจากกันซึ่งบางส่วนที่ฉันสงสัยว่าการฟื้นฟูแผนกำลังเกิดขึ้น ในขณะที่และบางส่วนที่ SPs มีประสิทธิภาพต่ำอย่างแท้จริงและจำเป็นต้องมีการประเมินซ้ำ


1
ขอบคุณสำหรับสิ่งนี้. การทำเครื่องหมายโพรซีเดอร์สำหรับการคอมไพล์ใหม่โดยใช้คำสั่ง sp_recompile ใช้ได้ผลสำหรับฉันเมื่อDBCC DROPCLEANBUFFERSและDBCC FREEPROCCACHEไม่มีความแตกต่าง
Steve

4

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


1
สวัสดี @Tundey ฉันแยกสายและยังใช้เวลาประมาณ 30 วินาทีและหมดเวลา ดังนั้นมันต้องมีอะไรบางอย่างระหว่างการสื่อสารฉันเดา?
iamserious

1

เพียงคอมไพล์กระบวนงานที่จัดเก็บใหม่ (ฟังก์ชันตารางในกรณีของฉัน) ก็ใช้ได้ผลสำหรับฉัน


1

เช่น @Zane กล่าวว่าอาจเกิดจากการดมกลิ่นพารามิเตอร์ ฉันพบพฤติกรรมเดียวกันและฉันดูแผนการดำเนินการของโพรซีเดอร์และคำสั่ง sp ทั้งหมดในแถว (คัดลอกคำสั่งทั้งหมดในรูปแบบโพรซีเดอร์ประกาศพารามิเตอร์เป็นตัวแปรและกำหนดค่าเดียวกันสำหรับตัวแปรเป็น พารามิเตอร์มี) อย่างไรก็ตามแผนการดำเนินการดูแตกต่างไปจากเดิมอย่างสิ้นเชิง การดำเนินการ sp ใช้เวลา 3-4 วินาทีและข้อความในแถวที่มีค่าเดียวกันจะถูกส่งกลับทันที

แผนการดำเนินการ

หลังจาก googling ฉันพบสิ่งที่น่าสนใจเกี่ยวกับพฤติกรรมนั้น: ช้าในแอปพลิเคชันเร็วใน SSMS?

เมื่อคอมไพล์โพรซีเดอร์ SQL Server ไม่ทราบว่าค่าของ @fromdate เปลี่ยนแปลง แต่รวบรวมโพรซีเดอร์ภายใต้สมมติฐานที่ว่า @fromdate มีค่า NULL เนื่องจากการเปรียบเทียบทั้งหมดกับ NULL yield UNKNOWN เคียวรีจึงไม่สามารถส่งคืนแถวใด ๆ ได้เลยถ้า @fromdate ยังคงมีค่านี้ในขณะรันไทม์ หาก SQL Server จะรับค่าอินพุตเป็นความจริงขั้นสุดท้ายก็สามารถสร้างแผนโดยมีเพียง Constant Scan ที่ไม่เข้าถึงตารางเลย (เรียกใช้แบบสอบถาม SELECT * FROM Orders WHERE OrderDate> NULL เพื่อดูตัวอย่างนี้) . แต่ SQL Server ต้องสร้างแผนที่ส่งคืนผลลัพธ์ที่ถูกต้องไม่ว่า @fromdate จะมีค่าใดในขณะรันไทม์ ในทางกลับกันไม่มีข้อผูกมัดในการสร้างแผนซึ่งดีที่สุดสำหรับค่าทั้งหมด ดังนั้นเนื่องจากสมมติฐานคือว่าจะไม่มีการส่งคืนแถว SQL Server จึงตั้งค่าสำหรับ Index Seek

ปัญหาคือฉันมีพารามิเตอร์ที่อาจปล่อยให้เป็นโมฆะและหากส่งผ่านเป็นโมฆะจะเริ่มต้นด้วยค่าเริ่มต้น

create procedure dbo.procedure
    @dateTo datetime = null
begin
    if (@dateTo is null)
    begin
        select @dateTo  = GETUTCDATE()
    end

    select foo
    from dbo.table
    where createdDate < @dateTo
end

หลังจากที่เปลี่ยนเป็น

create procedure dbo.procedure
    @dateTo datetime = null
begin
    declare @to datetime = coalesce(@dateTo, getutcdate())

    select foo
    from dbo.table
    where createdDate < @to
end 

มันกลับมาเหมือนมีเสน่ห์อีกครั้ง


0
--BEFORE
CREATE PROCEDURE [dbo].[SP_DEMO]
( 
    @ToUserId bigint=null
 )
AS
BEGIN
SELECT * FROM tbl_Logins WHERE LoginId = @ToUserId 
END
--AFTER CHANGING TO IT WORKING FINE
CREATE PROCEDURE [dbo].[SP_DEMO]
( 
    @ToUserId bigint=null
 )
AS
BEGIN
DECLARE @Toid bigint=null
SET @Toid=@ToUserId
SELECT * FROM tbl_Logins WHERE LoginId = @Toid 
END
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.