ประสิทธิภาพช้าแทรกแถวน้อยลงในตารางขนาดใหญ่


9

เรามีกระบวนการที่ใช้ข้อมูลจากร้านค้าและอัปเดตตารางสินค้าคงคลังทั่วทั้ง บริษัท ตารางนี้มีแถวสำหรับทุกร้านค้าตามวันที่และตามรายการ ที่ลูกค้าที่มีร้านค้ามากมายตารางนี้จะมีขนาดใหญ่มาก - ตามลำดับ 500 ล้านแถว

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

ลูกค้าร้องเรียนว่ากระบวนการใช้เวลานาน ฉันได้ทำโพรเซสและพบว่ามีหนึ่งแบบสอบถามที่ INSERT ในตารางนี้ใช้เวลานานกว่าที่คาดไว้ บางครั้ง INSERT นี้จะเสร็จสมบูรณ์ใน 30 วินาที

เมื่อฉันรันคำสั่ง ad-hoc SQL INSERT กับตารางนี้ (ล้อมรอบด้วย BEGIN TRAN และ ROLLBACK), ad-hoc SQL จะทำงานให้เสร็จสมบูรณ์ตามลำดับมิลลิวินาที

ข้อความค้นหาที่ทำงานช้าอยู่ด้านล่าง แนวคิดคือการบันทึก INSERT ที่ไม่ได้อยู่ที่นั่นและภายหลังเพื่อปรับปรุงพวกเขาในขณะที่เราคำนวณบิตของข้อมูล ขั้นตอนก่อนหน้าในกระบวนการระบุรายการที่จำเป็นต้องได้รับการปรับปรุงทำการคำนวณบางอย่างและยัดผลลัพธ์ลงในตาราง tempdb Update_Item_Work กระบวนการนี้ทำงานใน 10 เธรดแยกกันและแต่ละเธรดมี GUID ของตนเองใน Update_Item_Work

INSERT INTO Inventory
(
    Inv_Site_Key,
    Inv_Item_Key,
    Inv_Date,
    Inv_BusEnt_ID,
    Inv_End_WtAvg_Cost
)
SELECT DISTINCT
    UpdItemWrk_Site_Key,
    UpdItemWrk_Item_Key,
    UpdItemWrk_Date,
    UpdItemWrk_BusEnt_ID,
    (CASE UpdItemWrk_Set_WtAvg_Cost WHEN 1 THEN UpdItemWrk_WtAvg_Cost ELSE 0 END)
FROM tempdb..Update_Item_Work (NOLOCK)
WHERE UpdItemWrk_GUID = @GUID
AND NOT EXISTS
    -- Only insert for site/item/date combinations that don't exist
    (SELECT *
    FROM Inventory (NOLOCK)
    WHERE Inv_Site_Key = UpdItemWrk_Site_Key
    AND Inv_Item_Key = UpdItemWrk_Item_Key
    AND Inv_Date = UpdItemWrk_Date)

ตารางสินค้าคงคลังมี 42 คอลัมน์ซึ่งส่วนใหญ่จะติดตามปริมาณและการนับสำหรับการปรับสินค้าคงคลังที่หลากหลาย sys.dm_db_index_physical_stats กล่าวว่าแต่ละแถวมีขนาดประมาณ 242 ไบต์ดังนั้นฉันคาดว่าประมาณ 33 แถวจะพอดีกับหน้า 8 KB เดียว

ตารางถูกทำคลัสเตอร์บนข้อ จำกัด ที่ไม่ซ้ำกัน (Inv_Site_Key, Inv_Item_Key, Inv_Date) คีย์ทั้งหมดเป็น DECIMAL (15,0) และวันที่คือ SMALLDATETIME มีคีย์หลักของ IDENTITY (nonclustered) และ 4 ดัชนีอื่น ๆ ดัชนีทั้งหมดและข้อ จำกัด ของคลัสเตอร์ถูกกำหนดด้วยอย่างชัดเจน (FILLFACTOR = 90, PAD_INDEX = ON)

ฉันดูในล็อกไฟล์เพื่อนับการแยกหน้า ฉันวัดได้ประมาณ 1,027 แยกในดัชนีคลัสเตอร์และ 1,724 แยกในดัชนีอื่น แต่ฉันไม่ได้บันทึกช่วงเวลาที่เกิดขึ้น หนึ่งชั่วโมงครึ่งต่อมาฉันวัด 7,035 หน้าแยกบนดัชนีคลัสเตอร์

แผนแบบสอบถามที่ฉันบันทึกในตัวสร้างโปรไฟล์จะมีลักษณะดังนี้:

Rows         Executes     StmtText                                                                                                                                             
----         --------     --------                                                                                                                                             
490          1            Sequence                                                                                                                                             
0            1              |--Index Update
0            1              |    |--Collapse
0            1              |         |--Sort
0            1              |              |--Filter
996          1              |                   |--Table Spool                                                                                                                 
996          1              |                        |--Split                                                                                                                  
498          1              |                             |--Assert
0            0              |                                  |--Compute Scalar
498          1              |                                       |--Clustered Index Update(UK_Inventory)
498          1              |                                            |--Compute Scalar
0            0              |                                                 |--Compute Scalar
0            0              |                                                      |--Compute Scalar
498          1              |                                                           |--Compute Scalar
498          1              |                                                                |--Top
498          1              |                                                                     |--Nested Loops
498          1              |                                                                          |--Stream Aggregate
0            0              |                                                                          |    |--Compute Scalar
498          1              |                                                                          |         |--Clustered Index Seek(tempdb..Update_Item_Work)
498          498            |                                                                          |--Clustered Index Seek(Inventory)
0            1              |--Index Update(UX_Inv_Exceptions_Date_Site_Item)
0            1              |    |--Collapse
0            1              |         |--Sort
0            1              |              |--Filter
996          1              |                   |--Table Spool
490          1              |--Index Update(UX_Inv_Date_Site_Item)
490          1                   |--Collapse
980          1                        |--Sort
980          1                             |--Filter
996          1                                  |--Table Spool                                                                                       

เมื่อดูที่การสืบค้นกับ dmv ต่างๆฉันเห็นว่าการสืบค้นกำลังรออยู่ที่ PAGEIOLATCH_EX เป็นระยะเวลา 0 บนหน้าหนึ่งในตารางคลังโฆษณานี้ ฉันไม่เห็นการรอหรือการบล็อกล็อก

เครื่องนี้มีหน่วยความจำประมาณ 32 GB กำลังเรียกใช้ SQL Server 2005 Standard Edition แม้ว่าจะอัปเกรดเป็น 2008 R2 Enterprise Edition เร็ว ๆ นี้ ฉันไม่มีตัวเลขว่าตารางคลังโฆษณามีขนาดเท่าไหร่ในแง่ของการใช้ดิสก์ แต่ฉันสามารถขอรับได้ถ้าจำเป็น มันเป็นหนึ่งในตารางที่ใหญ่ที่สุดในระบบนี้

ฉันวิ่งแบบสอบถามกับ sys.dm_io_virtual_file_stats และเห็นรอเขียนเฉลี่ยกับ tempdb ที่จะขึ้นไป 1.1 วินาที ฐานข้อมูลที่จัดเก็บตารางนี้มีการรอเขียนโดยเฉลี่ยประมาณ ~ 350 ms แต่พวกเขารีสตาร์ทเซิร์ฟเวอร์ทุก ๆ 6 เดือนหรือมากกว่านั้นดังนั้นฉันจึงไม่รู้ว่าข้อมูลนี้เกี่ยวข้องหรือไม่ tempdb มีการแพร่กระจายมากกว่า 4 ไฟล์ที่แตกต่างกันพวกเขามี 3 ไฟล์ที่แตกต่างกันสำหรับฐานข้อมูลที่เก็บตารางสินค้าคงคลัง

เหตุใดแบบสอบถามนี้จึงใช้เวลานานในการแทรก INSERT สองสามแถวเมื่อรันด้วยเธรดที่แตกต่างกันจำนวนมากเมื่อ INSERT เดียวรวดเร็วมาก

- อัปเดต -

นี่คือหมายเลขเวลาในการตอบสนองต่อไดรฟ์รวมถึงการอ่านไบต์ อย่างที่คุณเห็นประสิทธิภาพของ tempdb นั้นน่าสงสัย ตารางคลังโฆษณาอยู่ใน PDICompany_252_01.mdf, PDICompany_252_01_Second.ndf หรือ PDICompany_252_01_Third.ndf

ReadLatencyWriteLatencyLatencyAvgBPerRead AvgBPerWriteAvgBPerTransferDriveDB                     physical_name
         42        1112    623       62171       67654          65147R:   tempdb                 R:\Microsoft SQL Server\Tempdb\tempdev1.mdf
         38        1101    615       62122       67626          65109S:   tempdb                 S:\Microsoft SQL Server\Tempdb\tempdev2.ndf
         38        1101    615       62136       67639          65123T:   tempdb                 T:\Microsoft SQL Server\Tempdb\tempdev3.ndf
         38        1101    615       62140       67629          65119U:   tempdb                 U:\Microsoft SQL Server\Tempdb\tempdev4.ndf
         25         341     71       92767       53288          87009X:   PDICompany             X:\Program Files\PDI\Enterprise\Databases\PDICompany_Third.ndf
         26         339     71       90902       52507          85345X:   PDICompany             X:\Program Files\PDI\Enterprise\Databases\PDICompany_Second.ndf
         10         231     90       98544       60191          84618W:   PDICompany_FRx         W:\Program Files\PDI\Enterprise\Databases\PDICompany_FRx.mdf
         61         137     68        9120        9181           9125W:   model                  W:\Microsoft SQL Server\MSSQL.3\MSSQL\Data\modeldev.mdf
         36         113     97        9376        5663           6419V:   model                  V:\Microsoft SQL Server\Logs\modellog.ldf
         22          99     34       92233       52112          86304W:   PDICompany             W:\Program Files\PDI\Enterprise\Databases\PDICompany.mdf
          9          20     10       25188        9120          23538W:   master                 W:\Microsoft SQL Server\MSSQL.3\MSSQL\Data\master.mdf
         20          18     19       53419       10759          40850W:   msdb                   W:\Microsoft SQL Server\MSSQL.3\MSSQL\Data\MSDBData.mdf
         23          18     19      947956       58304         110123V:   PDICompany_FRx         V:\Program Files\PDI\Enterprise\Databases\PDICompany_FRx_1.ldf
         20          17     17      828123       55295         104730V:   PDICompany             V:\Program Files\PDI\Enterprise\Databases\PDICompany.ldf
          5          13     13       12308        4868           5129V:   master                 V:\Microsoft SQL Server\Logs\mastlog.ldf
         11          13     13       22233        7598           8513V:   PDIMaster              V:\Program Files\PDI\Enterprise\Databases\PDIMaster.ldf
         14          11     13       13846        9540          12598W:   PDIMaster              W:\Program Files\PDI\Enterprise\Databases\PDIMaster.mdf
         13          11     11       22350        1107           1110V:   msdb                   V:\Microsoft SQL Server\Logs\MSDBLog.ldf
         17           9      9      745437       11821          23249V:   PDIFoundation          V:\Program Files\PDI\Enterprise\Databases\PDIFoundation.ldf
         34           8     31       29490       33725          30031W:   PDIFoundation          W:\Program Files\PDI\Enterprise\Databases\PDIFoundation.mdf
          5           8      8       61560       61236          61237V:   tempdb                 V:\Microsoft SQL Server\Logs\templog.ldf
         13           6     11        8370       35087          16785W:   SAHost_Company01       W:\Program Files\PDI\Enterprise\Databases\SAHostCompany.mdf
          2           6      5       56235       33667          38911W:   SAHost_Company01       W:\Program Files\PDI\Enterprise\Databases\SAHost_Company_01_log.LDF

ความคิดเห็นไม่ได้มีไว้สำหรับการอภิปรายเพิ่มเติม การสนทนานี้ได้รับการย้ายไปแชท
พอลไวท์ 9

คำตอบ:


4

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

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

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


1

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

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

 INSERT INTO i (...)
 SELECT DISTINCT ...             
   FROM tempdb..Update_Item_Work t (NOLOCK) -- nolock okay on read table
   left join Inventory i -- use without NOLOCK because PK is written inter-thread
     on i.Inv_Site_Key = t.UpdItemWrk_Site_Key
    and i.Inv_Item_Key = t.UpdItemWrk_Item_Key
    and i.Inv_Date = t.UpdItemWrk_Date
  where i.Inv_Site_Key is null   -- where not exist in inventory
    and UpdItemWrk_GUID = @GUID  -- for this thread

เวลาที่เรียกใช้เป็นฐานคุณอาจเรียกใช้อีกครั้งด้วยคำแนะนำการรวม ("เข้าร่วมซ้าย" -> "เข้าร่วมผสานซ้าย") เป็นความเป็นไปได้อื่น คุณอาจจะมีดัชนีในตาราง temp (UpdItemWrk_Site_Key, UpdItemWrk_Item_Key, UpdItemWrk_Date) สำหรับคำแนะนำการรวม

ฉันไม่ทราบว่า SQL Server 2008/2012 รุ่นที่ไม่ใช่ด่วนรุ่นใหม่นี้จะสามารถผสานแบบฟอร์มที่มีขนาดใหญ่ขึ้นแบบขนานโดยอัตโนมัติหรือไม่ซึ่งจะช่วยให้คุณสามารถลบการแบ่งพาร์ติชั่นที่ใช้ GUID ได้

เพื่อกระตุ้นให้การเข้าร่วมเกิดขึ้นเฉพาะกับรายการที่แตกต่างมากกว่าทุกรายการส่วนคำสั่ง "select different ... from ... " สามารถแปลงเป็น "select * from (select ชัด ... จาก ... )" ก่อน ดำเนินการต่อด้วยการเข้าร่วม สิ่งนี้อาจสร้างความแตกต่างที่เห็นได้ชัดเจนหากความแตกต่างกำลังกรองหลายแถว เครื่องมือเพิ่มประสิทธิภาพอาจเพิกเฉยต่อความพยายามนี้อีกครั้ง

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