ข้อผิดพลาดประสิทธิภาพการทำงานดัชนีดัชนี SQL Server 2008 datetime


11

เรากำลังใช้ SQL Server 2008 R2 และมีตารางที่มีขนาดใหญ่มาก (100M + แถว) ที่มีดัชนี id หลักและdatetimeคอลัมน์ที่มีดัชนีที่ไม่เป็นคลัสเตอร์ เราจะเห็นพฤติกรรมลูกค้า / เซิร์ฟเวอร์บางสูงผิดปกติขึ้นอยู่กับการใช้งานของนั้นorder byข้อโดยเฉพาะในคอลัมน์วันที่และเวลาการจัดทำดัชนี

ฉันอ่านข้อความต่อไปนี้: /programming/1716798/sql-server-2008-ordering-by-datetime-is-too-slow แต่มีอะไรเกิดขึ้นกับไคลเอนต์ / เซิร์ฟเวอร์มากกว่าสิ่งที่เป็น เริ่มอธิบายที่นี่

หากเราเรียกใช้แบบสอบถามต่อไปนี้ (แก้ไขเพื่อป้องกันเนื้อหาบางส่วน):

select * 
from [big table] 
where serial_number = [some number] 
order by test_date desc

แบบสอบถามหมดเวลาทุกครั้ง ใน SQL Server Profiler แบบสอบถามที่ดำเนินการจะมีลักษณะเช่นนี้กับเซิร์ฟเวอร์:

exec sp_cursorprepexec @p1 output,@p2 output,NULL,N'select * .....

ตอนนี้ถ้าคุณปรับเปลี่ยนแบบสอบถามให้พูดสิ่งนี้:

declare @temp int;
select * from [big table] 
where serial_number = [some number] 
order by test_date desc

Profiler ของเซิร์ฟเวอร์ SQL แสดงแบบสอบถามที่ดำเนินการซึ่งมีลักษณะเช่นนี้ไปยังเซิร์ฟเวอร์และจะทำงานทันที:

exec sp_prepexec @p1 output, NULL, N'declare @temp int;select * from .....

ตามความเป็นจริงคุณสามารถใส่ความคิดเห็นที่ว่างเปล่า ('-;') แทนข้อความประกาศที่ไม่ได้ใช้และได้รับผลลัพธ์เดียวกัน ดังนั้นในตอนแรกเราได้ชี้ไปที่ตัวประมวลผลล่วงหน้าของ sp เป็นสาเหตุของปัญหานี้ แต่ถ้าคุณทำสิ่งนี้:

select * 
from [big table] 
where serial_number = [some number] 
order by Cast(test_date as smalldatetime) desc

มันทำงานได้ทันทีเช่นกัน (คุณสามารถส่งเป็นdatetimeประเภทอื่นได้) ส่งคืนผลลัพธ์เป็นมิลลิวินาที และ profiler แสดงการร้องขอไปยังเซิร์ฟเวอร์ดังนี้:

exec sp_cursorprepexec @p1 output, @p2 output, NULL, N'select * from .....

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

เราพบปัญหานี้กันสักหน่อยแล้วและฉันเห็นปัญหาที่คล้ายกันซึ่งโพสต์จากคนอื่น ๆ แต่ก็ไม่มีใครทำลายมันลงไปถึงระดับนี้

มีคนอื่นเห็นพฤติกรรมนี้หรือไม่? ใครบ้างมีทางออกที่ดีกว่าการวาง SQL ที่ไม่มีความหมายไว้ข้างหน้าคำสั่ง select เพื่อเปลี่ยนพฤติกรรม? เนื่องจาก SQL Server ควรเรียกใช้คำสั่งโดยหลังจากรวบรวมข้อมูลแล้วดูเหมือนว่านี่เป็นจุดบกพร่องในเซิร์ฟเวอร์ที่ยังคงมีอยู่เป็นเวลานาน เราพบว่าพฤติกรรมนี้สอดคล้องกันทั่วทั้งตารางขนาดใหญ่ของเราและสามารถทำซ้ำได้

การแก้ไข:

ฉันควรเพิ่มการใส่forceseekเข้าไปยังทำให้ปัญหาหายไป

ฉันควรเพิ่มเพื่อช่วยให้ผู้ค้นหาข้อผิดพลาดการหมดเวลา ODBC คือ: [Microsoft] [โปรแกรมควบคุม ODBC SQL Server Server] การดำเนินการถูกยกเลิก

เพิ่ม 10/12/2012: ยังคงตามหาสาเหตุที่แท้จริง (พร้อมกับสร้างตัวอย่างเพื่อมอบให้กับ Microsoft ฉันจะข้ามโพสต์ผลลัพธ์ใด ๆ ที่นี่หลังจากที่ฉันส่ง) ฉันขุดลงไปในไฟล์ติดตาม ODBC ระหว่างคิวรี่ที่ใช้งานได้ (พร้อมกับเพิ่มข้อคิดเห็น / ประกาศคำสั่ง) และคิวรี่ที่ไม่ทำงาน ความแตกต่างพื้นฐานการติดตามถูกโพสต์ด้านล่าง มันเกิดขึ้นกับการเรียก SQLExtendedFetch โทรหลังจากการสนทนา SQLBindCol ทั้งหมดเสร็จสมบูรณ์ การเรียกล้มเหลวด้วยโค้ดส่งคืน -1 จากนั้นเธรดหลักจะเข้าสู่ SQLCancel เนื่องจากเราสามารถผลิตสิ่งนี้ได้ทั้งกับ Native Client และไดรเวอร์ ODBC ดั้งเดิมฉันยังคงชี้ประเด็นความเข้ากันได้ทางฝั่งเซิร์ฟเวอร์

(clip)
MSSQLODBCTester 1664-1718   EXIT  SQLBindCol  with return code 0 (SQL_SUCCESS)
        HSTMT               0x001EEA10
        UWORD                       16 
        SWORD                        1 <SQL_C_CHAR>
        PTR                0x03259030
        SQLLEN                    51
        SQLLEN *            0x0326B820 (0)

MSSQLODBCTester 1664-1718   ENTER SQLExtendedFetch 
        HSTMT               0x001EEA10
        UWORD                        1 <SQL_FETCH_NEXT>
        SQLLEN                     1
        SQLULEN *           0x032677C4
        UWORD *             0x032679B0

MSSQLODBCTester 1664-1fd0   ENTER SQLCancel 
        HSTMT               0x001EEA10

MSSQLODBCTester 1664-1718   EXIT  SQLExtendedFetch  with return code -1 (SQL_ERROR)
        HSTMT               0x001EEA10
        UWORD                        1 <SQL_FETCH_NEXT>
        SQLLEN                     1
        SQLULEN *           0x032677C4
        UWORD *             0x032679B0

        DIAG [S1008] [Microsoft][ODBC SQL Server Driver]Operation canceled (0) 

MSSQLODBCTester 1664-1fd0   EXIT  SQLCancel  with return code 0 (SQL_SUCCESS)
        HSTMT               0x001EEA10

MSSQLODBCTester 1664-1718   ENTER SQLErrorW 
        HENV                0x001E7238
        HDBC                0x001E7B30
        HSTMT               0x001EEA10
        WCHAR *             0x08BFFC5C
        SDWORD *            0x08BFFF08
        WCHAR *             0x08BFF85C 
        SWORD                      511 
        SWORD *             0x08BFFEE6

MSSQLODBCTester 1664-1718   EXIT  SQLErrorW  with return code 0 (SQL_SUCCESS)
        HENV                0x001E7238
        HDBC                0x001E7B30
        HSTMT               0x001EEA10
        WCHAR *             0x08BFFC5C [       5] "S1008"
        SDWORD *            0x08BFFF08 (0)
        WCHAR *             0x08BFF85C [      53] "[Microsoft][ODBC SQL Server Driver]Operation canceled"
        SWORD                      511 
        SWORD *             0x08BFFEE6 (53)

MSSQLODBCTester 1664-1718   ENTER SQLErrorW 
        HENV                0x001E7238
        HDBC                0x001E7B30
        HSTMT               0x001EEA10
        WCHAR *             0x08BFFC5C
        SDWORD *            0x08BFFF08
        WCHAR *             0x08BFF85C 
        SWORD                      511 
        SWORD *             0x08BFFEE6

MSSQLODBCTester 1664-1718   EXIT  SQLErrorW  with return code 100 (SQL_NO_DATA_FOUND)
        HENV                0x001E7238
        HDBC                0x001E7B30
        HSTMT               0x001EEA10
        WCHAR *             0x08BFFC5C
        SDWORD *            0x08BFFF08
        WCHAR *             0x08BFF85C 
        SWORD                      511 
        SWORD *             0x08BFFEE6
(clip)

เพิ่มเคส Microsoft Connect 10/12/2012:

https://connect.microsoft.com/SQLServer/feedback/details/767196/order-by-datetime-in-odbc-fails-for-clean-sql-statements#details

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


จะเกิดอะไรขึ้นถ้าคุณลองทำselect id, test_date from [big table] where serial_number = ..... order by test_date- ฉันแค่สงสัยว่าสิ่งSELECT *นั้นมีผลเสียต่อการแสดงของคุณหรือไม่ หากคุณมีดัชนีแบบ nonclustered test_dateและดัชนีแบบคลัสเตอร์บนid(สมมติว่าเป็นสิ่งที่เรียกว่า) การสืบค้นนี้ควรถูกครอบคลุมโดยดัชนีแบบ nonclustered นั้นดังนั้นจึงควรกลับอย่างรวดเร็ว
marc_s

ขออภัยจุดที่ดี ฉันควรรวมไว้ว่าเราพยายามแก้ไขพื้นที่คอลัมน์ที่เลือก (ลบ '*' ฯลฯ ) อย่างมากด้วยชุดค่าผสมต่างๆ พฤติกรรมที่อธิบายข้างต้นยืนยันผ่านการเปลี่ยนแปลงเหล่านั้น
DBtheDBA

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

สแต็กไคลเอนต์ใดถูกใช้ที่นี่ หากไม่มีข้อความติดตามทั้งหมดดูเหมือนว่าจะเป็นปัญหา ลองตัดสายเดิมเข้าไปด้านในsp_executesqlแล้วดูว่าเกิดอะไรขึ้น
Jon Seigel

1
แผนการดำเนินการที่ช้ามีลักษณะอย่างไร การดมกลิ่นพารามิเตอร์?
Martin Smith

คำตอบ:


6

ไม่มีเรื่องลึกลับคุณจะได้รับแผนการที่ไม่ดีอย่างแท้จริงโดยทั่วไปเนื่องจากไม่มีตัวเลือกที่ชัดเจนสำหรับดัชนีที่จะใช้ ในขณะที่น่าสนใจสำหรับคำสั่งย่อย ORDER BY และหลีกเลี่ยงการเรียงลำดับคุณไม่ได้ทำดัชนีกลุ่มในคอลัมน์ datetime เป็นตัวเลือกที่แย่มากสำหรับแบบสอบถามนี้ สิ่งที่จะทำให้ดัชนีดีมากสำหรับนี้(serial_number, test_date)แบบสอบถามจะเป็นหนึ่งใน ยิ่งไปกว่านั้นสิ่งนี้จะทำให้ผู้สมัครที่ดีมากสำหรับคีย์ดัชนีคลัสเตอร์

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


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

5
สิ่งนี้ยังไม่ได้อธิบายว่าเพราะเหตุใดการเพิ่มความคิดเห็นที่จุดเริ่มต้นของแบบสอบถามจึงส่งผลต่อระยะเวลาการเรียกใช้
cfradenburg

นอกจากนี้ตารางของเรายังมีการสอบถามหมายเลขซีเรียลอยู่เสมอไม่ใช่ test_date เรามีดัชนีที่ไม่คลัสเตอร์ในทั้งสองและคลัสเตอร์ในคอลัมน์ id ในตารางเท่านั้น มันเป็นแหล่งข้อมูลการดำเนินงานและการเพิ่มดัชนีคลัสเตอร์ในคอลัมน์อื่น ๆ จะทำให้การแยกหน้าและประสิทธิภาพการทำงานแย่ลง
DBtheDBA

1
@DBtheDBA: หากคุณต้องการอ้างสิทธิ์สำหรับ 'บั๊ก' คุณต้องทำการตรวจสอบและเปิดเผยข้อมูลที่เหมาะสม แน่นอนคีมาของตารางและสถิติการส่งออกของคุณทำตามวิธีการสร้างสคริปต์ของเมตาดาต้าฐานข้อมูลที่จำเป็นในการสร้างฐานข้อมูลสถิติเฉพาะใน SQL Server 2005 และใน SQL Server 2008โดยเฉพาะที่สำคัญทั้งหมดสคริปต์สถิติ : Script สถิติและ histograms เพิ่มเหล่านี้ไปยังข้อมูลโพสต์พร้อมกับขั้นตอนที่ทำให้เกิดปัญหาอีกครั้ง
Remus Rusanu

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

0

จัดทำเอกสารรายละเอียดของวิธีการสร้างข้อผิดพลาดและส่งไปที่ connect.microsoft.com ฉันตรวจสอบแล้วและไม่สามารถมองเห็นสิ่งใดที่นั่นเกี่ยวข้องกับสิ่งนี้


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

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

0

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

นี่คือตันของรายละเอียดเกี่ยวกับวิธีการ SQL ไม่แคชแผน

คัดลอกรายละเอียด: มีคนเรียกใช้คิวรีนั้นก่อนหน้านี้สำหรับบางหมายเลข [บางหมายเลข] SQL ดูค่าที่ให้ไว้ดัชนีและสถิติสำหรับตาราง / คอลัมน์ที่เกี่ยวข้อง ฯลฯ และสร้างแผนที่ทำงานได้ดีสำหรับ [บางหมายเลข] จากนั้นแคชแผนเรียกใช้และให้ผลลัพธ์กลับไปยังผู้โทร

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

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

ตัวอย่างรวดเร็ว: เลือก * จาก [คน] โดยที่นามสกุล = 'SMITH' - นามสกุลที่นิยมมากในสหรัฐอเมริกา GO เลือก * จาก [คน] โดยที่นามสกุล = 'BONAPARTE' - ไม่ใช่นามสกุลที่นิยมในสหรัฐอเมริกา

เมื่อรัน BONAPARTE เคียวรีแผนที่สร้างสำหรับ SMITH จะถูกนำมาใช้ใหม่ หาก SMITH ทำให้เกิดการสแกนตาราง (ซึ่งอาจดีถ้าแถวในตารางเป็น 99% SMITH) BONAPARTE จะได้รับการสแกนตารางเช่นกัน ถ้า BONAPARTE รันก่อน SMITH อาจมีการสร้างและใช้แผนโดยใช้ดัชนีจากนั้นใช้อีกครั้งสำหรับ SMITH (ซึ่งอาจดีกว่าเมื่อสแกนตาราง) ผู้คนอาจไม่สังเกตเห็นว่าประสิทธิภาพของ SMITH นั้นแย่เนื่องจากพวกเขาคาดหวังว่าประสิทธิภาพที่ไม่ดีเนื่องจากทั้งตารางต้องอ่านและอ่านดัชนีและการกระโดดไปที่ตารางไม่ได้สังเกตเห็นโดยตรง

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

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

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

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

หากวิธีนี้ไม่ได้ผลสิ่งที่สามที่ต้องลองคือบังคับแผนใหม่ทุกครั้งที่คุณเรียกใช้คำสั่งโดยใช้คำสำคัญ RECOMPILE:

เลือก * จาก [ตารางใหญ่] โดยที่ serial_number = [บางหมายเลข] สั่งซื้อโดย test_date desc OPTION (RECOMPILE)

มีบทความอธิบายเป็นสถานการณ์ที่คล้ายกันที่นี่ ตรงไปตรงมาฉันเพิ่งเห็น RECOMPILE นำไปใช้กับกระบวนงานที่เก็บไว้ก่อนหน้านี้ แต่ดูเหมือนว่าจะทำงานกับคำสั่ง SELECT "ปกติ" Kimberly Tripp ไม่เคยพาฉันผิด

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


เพื่อให้ครอบคลุมข้อกังวลเหล่านี้: 1. สถิติได้รับการอัพเดทแล้ว, กำลังมีการอัพเดท 2. เราได้พยายามจัดทำดัชนีในหลาย ๆ วิธี (ครอบคลุมดัชนี ฯลฯ ) แต่ปัญหาดูเหมือนว่าจะเชื่อมโยงกับการorder byใช้งานกับดัชนีวันที่มากขึ้นโดยเฉพาะ 3. ลองใช้ความคิดของคุณกับตัวเลือก RECOMPILE แต่ก็ยังล้มเหลวซึ่งทำให้ฉันประหลาดใจนิดหน่อยฉันหวังว่ามันจะได้ผลแม้ว่าฉันจะไม่รู้ว่ามันเป็นทางออกสำหรับการผลิตหรือไม่
DBtheDBA
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.