SQL: INSERT มีอะไรช้าลงถ้าไม่ใช่ CPU หรือ IO


19

เรามีฐานข้อมูลสำหรับผลิตภัณฑ์ที่เขียนหนัก เราเพิ่งซื้อเครื่องเซิร์ฟเวอร์ใหม่พร้อม SSD เพื่อช่วย ด้วยความประหลาดใจของเราการแทรกไม่เร็วกว่าบนเครื่องเก่าของเราที่มีการจัดเก็บช้ากว่ามาก ในระหว่างการเปรียบเทียบเราสังเกตเห็นว่าอัตรา IO ที่แสดงโดยกระบวนการ SQL Server นั้นต่ำมาก

ตัวอย่างเช่นฉันรันสคริปต์ที่พบในหน้านี้ยกเว้นว่าฉันได้เพิ่ม BEGIN TRAN และ COMMIT รอบลูป ที่ดีที่สุดฉันจะเห็นการใช้งานดิสก์ถึง 7Mb / s ในขณะที่ CPU แทบจะไม่ได้สัมผัส 5% เซิร์ฟเวอร์มีการติดตั้ง 64Gb และใช้ 10 เวลาการทำงานทั้งหมดคือ 2 นาที 15 วินาทีสำหรับการโทรครั้งแรกจนถึงประมาณ 1 นาทีสำหรับการโทรครั้งต่อไป ฐานข้อมูลเป็นการกู้คืนอย่างง่ายและไม่ได้ใช้งานในระหว่างการทดสอบ ฉันวางโต๊ะระหว่างการโทรแต่ละครั้ง

ทำไมสคริปต์ง่าย ๆ นี้ถึงช้าจัง ฮาร์ดแวร์แทบจะไม่มีการใช้เลย ทั้งเครื่องมือการเปรียบเทียบดิสก์เฉพาะและ SQLIO ระบุว่า SSD ทำงานอย่างถูกต้องด้วยความเร็วสูงกว่า 500Mb / s สำหรับทั้งการอ่านและการเขียน ฉันเข้าใจว่าการเขียนแบบสุ่มช้ากว่าการเขียนตามลำดับ แต่ฉันคาดว่าการแทรกแบบง่ายเช่นนี้ไปยังตารางที่ไม่มีการทำดัชนีแบบคลัสเตอร์จะเร็วกว่ามาก

ในที่สุดสถานการณ์ของเรามีความซับซ้อนมากขึ้น แต่ฉันรู้สึกว่าฉันต้องเข้าใจกรณีง่าย ๆ ก่อน สรุปแอปพลิเคชันของเราจะลบข้อมูลเก่าจากนั้นใช้ SqlBulkCopy เพื่อคัดลอกข้อมูลใหม่ไปยังตารางชั่วคราวทำการกรองบางอย่างและในที่สุดก็ใช้ MERGE และ / หรือ INSERT INTO ขึ้นอยู่กับกรณีที่คัดลอกข้อมูลไปยังตารางสุดท้าย

-> แก้ไข 1: ฉันทำตามขั้นตอนที่เชื่อมโยงโดย Martin Smith และฉันได้รับผลลัพธ์ต่อไปนี้:

[Wait Type]  [Wait Count] [Total Wait (ms)] [T. Resource Wait (ms)] [T. Signal Wait (ms)]
NETWORK_IO          5008              46735                 46587        148
LOGBUFFER           901               5994                  5977         17
PAGELATCH_UP        40                866                   865          1
SOS_SCHEDULER_YIELD 53279             219                   121          98
WRITELOG            5                 145                   145          0
PAGEIOLATCH_UP      4                 58                    58           0
LATCH_SH            5                 0                     0            0

ฉันพบว่ามันแปลก NETWORK_IO ใช้เวลาส่วนใหญ่พิจารณาว่าไม่มีผลลัพธ์ที่จะแสดงและไม่มีข้อมูลที่จะถ่ายโอนที่อื่นนอกเหนือไปจากไฟล์ SQL ชนิด NETWORK_IO รวมถึง IO ทั้งหมดหรือไม่

-> แก้ไข 2: ฉันสร้างดิสก์ RAM 20Gb และติดตั้งฐานข้อมูลจากที่นั่น เวลาที่ดีที่สุดที่ฉันมีบน SSD คือ 48 วินาทีโดยมี RAM ดิสก์ลดลงเหลือ 37 วินาที NETWORK_IO ยังคงเป็นการรอคอยที่ยิ่งใหญ่ที่สุด ความเร็วในการเขียนสูงสุดไปยังดิสก์ RAM คือประมาณ 250Mb / s ในขณะที่สามารถทำหลายกิกะไบต์ต่อวินาที มันยังไม่ได้ใช้ CPU มากดังนั้นการถือ SQL คืออะไร



3
NETWORK_IOอาจจะมาจาก 3 ล้าน "1 แถว (s) ได้รับผลกระทบ" ข้อความที่ถูกส่งกลับ คุณลองเพิ่มSET NOCOUNT ONไปยังสคริปต์หรือไม่
Martin Smith

ใช่ฉันเพิ่ม NOCOUNT
Djof

2
แปลก. ฉันจะไม่คาดหวังอะไรมากกับกิจกรรมของเครือข่ายเลย คุณลบไฟล์กิจกรรมเสริมเก่าระหว่างการรันหรือไม่? สคริปต์ที่อ่านพวกเขาใช้ไวด์การ์ดEE_WaitStats*.xelดังนั้นการ์ดเก่าจะปนเปื้อนผลลัพธ์ของคุณ
Martin Smith

ดีโทรฉันจะอัปเดตผลลัพธ์พรุ่งนี้
Djof

คำตอบ:


9

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

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

ในกรณีของฉันมันใช้เวลา 36 วินาทีในการแทรก 3 ล้านแถว นั่นหมายถึง 36/30000000 = 0.000012 วินาทีต่อแถว มันค่อนข้างเร็ว ในระบบของฉันใช้เวลาเพียง 0.000012 ในการทำตามขั้นตอนทั้งหมดที่จำเป็น

วิธีเดียวที่จะทำให้เสร็จเร็วขึ้นคือเริ่มเซสชันที่สองแบบขนาน

ถ้าฉันเริ่มต้น 2 ครั้งในแบบคู่ขนานทั้งสองแทรก 15 ล้านแทรก ทั้งสองเสร็จใน 18 วินาที ฉันสามารถขยายขนาดได้มากขึ้น แต่การตั้งค่าการทดสอบปัจจุบันของฉันมีการกด cpu 95% ด้วยสองเซสชันแบบขนานดังนั้นการทำ 3 จะทำให้ผลที่ได้เบี่ยงเบนไปเนื่องจากฉันจะตีคอขวดของ CPU

ถ้าฉันเริ่มเซสชันคู่ขนาน 2 ครั้งแทรกทั้ง 3 ล้านแถวทั้งคู่จะเสร็จสิ้นใน 39 วินาที ตอนนี้ก็คือ 6 ล้านแถวใน 39 วินาที

โอเคที่ยังเหลือเราด้วย NETWORK_IO รอปรากฏขึ้น

NETWORK_IO รอเพิ่มโดยความจริงที่ว่าคุณใช้กิจกรรมเพิ่มเติมเพื่อติดตามพวกเขา ในกรณีของฉันการแทรกใช้เวลา 36 วินาที (โดยเฉลี่ย) เมื่อใช้วิธีเหตุการณ์เพิ่มเติม (จากลิงก์ด้านบนในความคิดเห็นแรก) นี่คือสิ่งที่ลงทะเบียน:

Wait Type             Wait Count  Total Wait Time (ms) Total Resource Wait Time (ms) Total Signal Wait Time (ms)
NETWORK_IO            3455        68808                68802                         6
PAGEIOLATCH_SH        3           64                   64                            0
PAGEIOLATCH_UP        12          58                   58                            0
WRITE_COMPLETION      8           15                   15                            0
WRITELOG              3           9                    9                             0
PAGELATCH_UP          2           4                    4                             0
SOS_SCHEDULER_YIELD   32277       1                    0                             1
IO_COMPLETION         8           0                    0                             0
LATCH_SH              3           0                    0                             0
LOGBUFFER             1           0                    0                             0

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

ถ้าฉันไม่ได้ใช้เหตุการณ์เพิ่มเติม แต่เพียงแค่สถิติการรอ DMVs ในอินสแตนซ์ที่เงียบสงบ (ด้วยฉันเพียงแค่เรียกใช้การแทรก) ฉันได้รับสิ่งนี้:

Wait Type                   Wait Count  Total Wait Time (ms)  Total Resource Wait Time (ms) Signal Resource Wait Time (ms)
SOS_SCHEDULER_YIELD             8873                 0.21                                    0.01                                    0.20
PAGEIOLATCH_UP                  3                    0.02                                    0.02                                    0.00
PREEMPTIVE_OS_AUTHENTICATIONOPS 17                   0.02                                    0.02                                    0.00
PAGEIOLATCH_SH                  1                    0.00                                    0.00                                    0.00

ดังนั้น NETWORK_IO ที่คุณเห็นในบันทึกเหตุการณ์แบบขยายไม่เกี่ยวข้องกับวงวนการแทรกของคุณ (หากคุณไม่เปิดใช้งาน nocount คุณจะมีเครือข่าย async ขนาดใหญ่ที่รอ IO มาร์ติน +1)

อย่างไรก็ตามฉันไม่รู้ว่าทำไม NETWORK_IO แสดงขึ้นในการติดตามกิจกรรมเพิ่มเติม ตรวจสอบให้แน่ใจว่าการเขียนไปยังไฟล์ async เป้าหมายของเหตุการณ์สะสม ASYNC_NETWORK_IO แต่แน่นอนว่าทั้งหมดนี้ทำบน SPID ที่แตกต่างจากนั้นก็เป็นสิ่งที่เรากำลังทำการกรอง ฉันอาจถามคำถามนี้เป็นคำถามใหม่ด้วยตัวเอง)


1
"คุณกำลังตีเพดานประสิทธิภาพโดยที่คุณไม่เห็นคอขวดของทรัพยากรเพราะคุณมีขีด จำกัด ของสิ่งที่เป็นไปได้ในการประมวลผลภายในหนึ่งเธรดเดี่ยวเซสชัน": คุณกำลังอธิบายคอขวด CPU 100% (บนแกนเดียว) หากไม่มีคอขวดระบบจะทำงานเร็วขึ้นดังนั้นจะต้องมีสิ่งอื่นที่จะเล่น
Remus Rusanu

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

9

โดยปกติคุณเริ่มต้นด้วยการมองหาที่sys.dm_exec_requestsเฉพาะที่wait_time, wait_typeและwait_resourceสำหรับการร้องขอ INSERT คุณ (s) สิ่งนี้จะให้สิ่งบ่งชี้อย่างชัดเจนว่ากำลังบล็อก INSERT อยู่ ผลลัพธ์จะระบุว่าการล็อกการล็อกเหตุการณ์การเติบโตของไฟล์การรอการบันทึกล็อกการช่วงชิงการจัดสรร (แสดงเป็นการโต้แย้งการสลักหน้า PFS) ฯลฯ ฯลฯ เมื่อคุณวัดแล้วให้อัปเดตคำถามของคุณตามลำดับ ฉันขอแนะนำให้คุณหยุดตอนนี้และอ่านวิธีการแก้ไขปัญหาWaits and Queuesก่อนที่จะดำเนินการต่อ


3

ฉันรันสคริปต์ทดสอบที่หน้าลิงก์ใน OP ด้วย BEGIN TRAN / COMMIT รอบลูป บนเครื่องของฉันมันใช้เวลา 1:28 เพื่อทำให้เสร็จในครั้งแรก

จากนั้นฉันย้ายคำสั่งทั้งสองออกไปนอกลูป:

SELECT @Random = ROUND(((@Upper - @Lower -1) * RAND() + @Lower), 0)
SET @InsertDate = DATEADD(dd, @Random, GETDATE())

จะแล้วเสร็จภายใน 28 วินาทีหลังจากนั้น

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

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


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

1

อีก DMV ที่ผมใช้ในการระบุความช้าเป็นsys.dm_os_waiting_tasks หากการสืบค้นของคุณไม่ได้ใช้ CPU มากคุณสามารถค้นหาข้อมูลเพิ่มเติมเกี่ยวกับการรอจาก DMV นี้


0

ฉันกำลังตรวจสอบรายการกิจกรรมการรอคอยสำหรับ sql 2008 และฉันไม่เห็น NETWORK_IO อยู่ในรายการ: http://technet.microsoft.com/en-us/library/ms179984(v=sql.100).aspx

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

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

ในตอนท้ายของวันมีเพียงไม่กี่คอขวดทรัพยากรที่เป็นไปได้: หน่วยความจำ, CPU, I / O ดิสก์เครือข่ายและล็อค คุณระบุว่า CPU และ I / O ไม่ใช่ปัญหาและคุณมีเหตุการณ์รอของ NETWORK_IO ดังนั้นฉันขอแนะนำให้คุณดูการ์ด NIC เหล่านั้นก่อน


1
NETWORK_IOแสดงขึ้นเนื่องจาก OP จะใช้เหตุการณ์ขยาย ไม่เคยมีการอัพเดทในsys.dm_xe_map_values
Martin Smith

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

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