เกี่ยวกับวิธีการผมเชื่อว่าคุณกำลังเห่า b-tree ;-) ที่ไม่ถูกต้อง
สิ่งที่เรารู้:
อันดับแรกให้รวบรวมและตรวจสอบสิ่งที่เรารู้เกี่ยวกับสถานการณ์:
สิ่งที่เราสามารถคาดเดาได้:
ต่อไปเราสามารถดูจุดข้อมูลเหล่านี้ทั้งหมดเข้าด้วยกันเพื่อดูว่าเราสามารถสังเคราะห์รายละเอียดเพิ่มเติมที่จะช่วยให้เราหาคอขวดหนึ่งขวดหรือมากกว่าและชี้ไปที่โซลูชันหรืออย่างน้อยก็ออกกฎบางอย่างที่เป็นไปได้
ทิศทางปัจจุบันของความคิดในความคิดเห็นคือปัญหาสำคัญคือการถ่ายโอนข้อมูลระหว่าง SQL Server และ Excel เป็นเช่นนั้นจริงหรือ หากมีการเรียกกระบวนงานที่เก็บไว้สำหรับแต่ละ 800,000 แถวและใช้เวลา 50 ms ต่อการโทรแต่ละครั้ง (เช่นต่อแต่ละแถว) ซึ่งจะเพิ่มขึ้นเป็น 40,000 วินาที (ไม่ใช่ ms) และนั่นเทียบเท่ากับ 666 นาที (hhmm ;-) หรือเพียง 11 ชั่วโมง ทว่ากระบวนการทั้งหมดนี้ใช้เวลาเพียง 7 ชั่วโมงในการดำเนินการ เรามีเวลาทั้งหมด 4 ชั่วโมงแล้วและเรายังเพิ่มเวลาเพื่อทำการคำนวณหรือบันทึกผลลัพธ์กลับไปยัง SQL Server ดังนั้นสิ่งที่ไม่ถูกต้องที่นี่
ดูคำจำกัดความของกระบวนการจัดเก็บมีเพียงพารามิเตอร์ป้อนเข้า@FileID
เท่านั้น @RowID
ไม่มีตัวกรองใด ๆ ดังนั้นฉันสงสัยว่าหนึ่งในสองสถานการณ์ต่อไปนี้จะเกิดขึ้น:
- ขั้นตอนการจัดเก็บนี้ไม่ได้รับการเรียกจริง ๆ สำหรับแต่ละแถว แต่จะมีการเรียกต่อแต่ละแถว
@FileID
ซึ่งจะมีความยาวประมาณ 4000 แถว หากจำนวนแถวที่ระบุไว้ 4,000 คืนเป็นจำนวนที่สอดคล้องกันจะมีเพียง 200 แถวใน 800,000 แถว และการประหารชีวิต 200 ครั้งโดยใช้ 50 มิลลิวินาทีในแต่ละจำนวนใช้เวลาเพียง 10 วินาทีจาก 7 ชั่วโมงนั้น
- หากขั้นตอนการจัดเก็บนี้ได้รับการเรียกสำหรับทุกแถวจริง ๆ แล้วจะไม่เป็นครั้งแรกที่มีการ
@FileID
ส่งผ่านใหม่ใช้เวลานานกว่าเล็กน้อยในการดึงแถวใหม่เข้าสู่ Buffer Pool แต่โดยทั่วไปแล้วการประมวลผล 3999 ครั้งถัดไป แคชใช่ไหม
ผมคิดว่าการมุ่งเน้นเกี่ยวกับเรื่องนี้ "กรอง" ขั้นตอนการเก็บหรือการถ่ายโอนข้อมูลใด ๆ จาก SQL Server ไปยัง Excel เป็นปลาชนิดหนึ่งสีแดง
ในขณะนี้ฉันคิดว่าตัวชี้วัดที่เกี่ยวข้องที่สุดเกี่ยวกับประสิทธิภาพการทำงานที่ขาดความดแจ่มใสคือ:
- มี 800,000 แถว
- การดำเนินการทำงานได้ครั้งละหนึ่งแถว
- ข้อมูลจะถูกบันทึกกลับไปยัง SQL Server ดังนั้น "[ใช้] ค่าจากบางคอลัมน์เพื่อจัดการคอลัมน์อื่น ๆ " [my em phas is ;-)]
ฉันสงสัยว่า:
- ในขณะที่มีห้องสำหรับปรับปรุงในการดึงข้อมูลและการคำนวณทำให้ดีขึ้นเหล่านั้นจะไม่ลดลงอย่างมีนัยสำคัญในเวลาการประมวลผล
- คอขวดใหญ่กำลังออก
UPDATE
แถลงการณ์แยกต่างหาก 800,000 ข้อความซึ่งเป็นธุรกรรมแยกกัน 800,000 รายการ
คำแนะนำของฉัน (ขึ้นอยู่กับข้อมูลที่มีอยู่ในปัจจุบัน):
การปรับปรุงที่ใหญ่ที่สุดของคุณคือการอัปเดตหลายแถวพร้อมกัน (เช่นในการทำธุรกรรมเดียว) คุณควรอัพเดตกระบวนการของคุณให้ทำงานในรูปแบบของแต่ละกระบวนการFileID
แทนที่จะเป็นแต่ละRowID
กระบวนการ ดังนั้น:
- อ่านแถวทั้งหมด 4000 แถว
FileID
ในแถวลำดับ
- อาร์เรย์ควรมีองค์ประกอบที่เป็นตัวแทนของเขตข้อมูลที่ถูกจัดการ
- วนรอบอาร์เรย์เพื่อประมวลผลแต่ละแถวเหมือนที่คุณทำอยู่ในปัจจุบัน
- เมื่อ
FileID
คำนวณแถวทั้งหมดในอาร์เรย์แล้ว (เช่นสำหรับรายการนี้):
- เริ่มทำธุรกรรม
- เรียกแต่ละการปรับปรุงต่อแต่ละ
RowID
- หากไม่มีข้อผิดพลาดให้ทำธุรกรรม
- หากมีข้อผิดพลาดเกิดขึ้นการย้อนกลับและการจัดการอย่างเหมาะสม
หากดัชนีคลัสเตอร์ของคุณยังไม่ได้กำหนดไว้(FileID, RowID)
ดังนั้นคุณควรพิจารณา (ตามที่ @MikaelEriksson แนะนำในการแสดงความคิดเห็นในคำถาม) มันจะไม่ช่วย UPDATEs เดี่ยวเหล่านี้ แต่ที่จะปรับปรุงการดำเนินงานรวมดังกล่าวเป็นสิ่งที่คุณกำลังทำในสิ่งที่ "กรอง" FileID
ขั้นตอนการเก็บน้อยเล็กน้อยเนื่องจากพวกเขาจะขึ้นอยู่ทั้งหมดบน
คุณควรพิจารณาย้ายตรรกะไปเป็นภาษาที่รวบรวม ฉันอยากจะแนะนำให้สร้างแอป. NET WinForms หรือแม้แต่แอพคอนโซล ฉันชอบแอพคอนโซลเพราะมันง่ายต่อการกำหนดเวลาผ่าน SQL Agent หรือ Windows Scheduled Tasks มันไม่สำคัญว่าจะทำใน VB.NET หรือ C # VB.NET อาจเหมาะสมกับผู้พัฒนาของคุณมากขึ้น แต่จะมีช่วงการเรียนรู้อยู่บ้าง
ฉันไม่เห็นเหตุผลใด ๆ ณ จุดนี้เพื่อย้ายไปยัง SQLCLR หากอัลกอริทึมเปลี่ยนแปลงบ่อยนั่นจะทำให้เกิดความรำคาญต้องปรับใช้แอสเซมบลีตลอดเวลาอีกครั้ง การสร้างแอป Console ขึ้นมาใหม่และวาง. exe ไว้ในโฟลเดอร์แชร์ที่เหมาะสมบนเครือข่ายเพื่อให้คุณเรียกใช้โปรแกรมเดียวกันและมันเพิ่งเกิดขึ้นเสมอและทันสมัยอยู่เสมอ
ฉันไม่คิดว่าการย้ายการประมวลผลอย่างสมบูรณ์ไปยัง T-SQL จะช่วยได้ถ้าปัญหานั้นเป็นสิ่งที่ฉันสงสัยและคุณเพียงแค่ทำการอัพเดทครั้งละหนึ่งครั้ง
หากการประมวลผลถูกย้ายไปยัง. NET คุณสามารถใช้พารามิเตอร์ที่มีมูลค่าของตาราง (TVPs) เพื่อที่คุณจะส่งผ่านอาร์เรย์ไปยังกระบวนงานที่เก็บไว้ซึ่งจะเรียกใช้การรวมUPDATE
นั้นกับตัวแปรตาราง TVP และเป็นธุรกรรมเดียว . TVP ควรเร็วกว่าการINSERT
จัดกลุ่ม4000 s เป็นธุรกรรมเดียว แต่กำไรที่ได้มาจากการใช้ TVP มากกว่า 4,000 INSERT
รายการใน 1 ธุรกรรมนั้นไม่ได้มีนัยสำคัญเท่ากับการปรับปรุงที่เห็นเมื่อย้ายจาก 800,000 ธุรกรรมแยกต่างหากไปเป็น 200 ธุรกรรมของ 4000 แถวต่อครั้ง
ตัวเลือก TVP นั้นไม่มีอยู่ในฝั่ง VBA แต่มีบางคนที่ทำงานด้วยวิธีการทดสอบที่คุ้มค่า:
ฉันจะปรับปรุงประสิทธิภาพของฐานข้อมูลได้อย่างไรเมื่อไปจาก VBA เป็น SQL Server 2008 R2
หากตัวกรอง proc ใช้เฉพาะFileID
ในWHERE
ข้อและถ้าเรียกว่า proc จริง ๆ ต่อแต่ละแถวคุณสามารถประหยัดเวลาในการประมวลผลได้โดยการแคชผลลัพธ์ของการเรียกใช้ครั้งแรกและใช้ส่วนที่เหลือของแถวต่อFileID
ไป ขวา?
เมื่อคุณได้รับการประมวลผลที่ทำต่อ FileID , แล้วเราสามารถเริ่มต้นการพูดคุยเกี่ยวกับการประมวลผลแบบขนาน แต่นั่นอาจไม่จำเป็น ณ จุดนั้น :) ระบุว่าคุณกำลังเผชิญกับ 3 ส่วนที่ไม่เหมาะเป็นอย่างยิ่ง: Excel, VBA, และธุรกรรม 800k, การพูดคุยของ SSIS, หรือรูปสี่เหลี่ยมด้านขนานหรือผู้รู้อะไรจะได้รับการปรับให้เหมาะสมก่อน / สิ่งที่ประเภทม้า . หากเราสามารถทำให้กระบวนการ 7 ชั่วโมงนี้ลดลงเหลือ 10 นาทีหรือน้อยกว่าคุณจะยังคงคิดหาวิธีเพิ่มเติมเพื่อให้เร็วขึ้นหรือไม่? มีเวลาในการบรรลุเป้าหมายที่คุณมีอยู่ในใจหรือไม่? โปรดทราบว่าเมื่อการประมวลผลเสร็จสิ้นต่อ FileID พื้นฐานถ้าคุณมี VB.NET Console App (เช่น command-line .EXE) จะไม่มีอะไรหยุดคุณจากการรัน FileIDs ในเวลาไม่กี่ครั้ง :) ไม่ว่าจะผ่านขั้นตอน CmdExec ของ SQL Agent หรือ Windows Scheduled Tasks เป็นต้น
และคุณสามารถใช้วิธี "แบ่งเป็นระยะ" และทำการปรับปรุงได้ตลอดเวลา เช่นเริ่มต้นด้วยการอัปเดตต่อFileID
และด้วยการใช้หนึ่งธุรกรรมสำหรับกลุ่มนั้น จากนั้นดูว่าคุณสามารถทำให้ TVP ทำงานได้หรือไม่ จากนั้นดูเกี่ยวกับการรับรหัสนั้นและย้ายไปยัง VB.NET (และ TVPs ทำงานใน. NET เพื่อที่จะได้พอร์ตอย่างสวยงาม)
สิ่งที่เราไม่ทราบว่ายังสามารถช่วย:
- "ตัวกรอง" กระบวนงานที่เก็บไว้ทำงานต่อ RowIDหรือต่อ FileIDหรือไม่ เรามีคำจำกัดความที่ครบถ้วนของกระบวนงานที่เก็บไว้หรือไม่?
- สคีมาแบบเต็มของตาราง ตารางนี้กว้างเท่าไหร่ มีฟิลด์ความยาวแปรผันจำนวนเท่าใด NULLable มีกี่เขตข้อมูล ถ้ามีค่า NULLable มี NULL จำนวนเท่าใด
- ดัชนีสำหรับตารางนี้ มันแบ่งพาร์ติชันหรือไม่ กำลังใช้การบีบอัด ROW หรือ PAGE หรือไม่
- ตารางนี้มีขนาดเท่าใดสำหรับ MB / GB
- การบำรุงรักษาดัชนีมีการจัดการอย่างไรสำหรับตารางนี้ ดัชนีมีการแยกส่วนอย่างไร สถิติมีการอัปเดตอย่างไร
- กระบวนการอื่นใดที่เขียนลงในตารางนี้ในขณะที่กระบวนการนี้ใช้เวลา 7 ชั่วโมงหรือไม่? แหล่งที่มาของความขัดแย้ง
- กระบวนการอื่นใดที่อ่านจากตารางนี้ในขณะที่กระบวนการนี้ใช้เวลา 7 ชั่วโมงหรือไม่? แหล่งที่มาของความขัดแย้ง
อัปเดต 1:
**ดูเหมือนจะมีความสับสนเกี่ยวกับสิ่งที่ VBA (Visual Basic สำหรับแอปพลิเคชัน) และสิ่งที่สามารถทำได้ด้วยดังนั้นนี่เป็นเพียงเพื่อให้แน่ใจว่าเราทุกคนอยู่ในหน้าเว็บเดียวกัน:
อัปเดต 2:
อีกหนึ่งจุดที่ควรพิจารณา: การเชื่อมต่อถูกจัดการอย่างไร? รหัส VBA เปิดและปิดการเชื่อมต่อต่อการดำเนินการแต่ละครั้งหรือไม่หรือเป็นการเปิดการเชื่อมต่อเมื่อเริ่มต้นกระบวนการและปิดเมื่อสิ้นสุดกระบวนการ (เช่น 7 ชั่วโมงต่อมา) หรือไม่ แม้ว่าจะมีการรวมการเชื่อมต่อ (ซึ่งโดยค่าเริ่มต้นควรเปิดใช้งานสำหรับ ADO) ก็ยังคงมีผลกระทบค่อนข้างมากระหว่างการเปิดและปิดหนึ่งครั้งซึ่งต่างจากการเปิดและปิดทั้ง 800,200 หรือ 1,600,000 ครั้ง ค่าเหล่านั้นจะขึ้นอยู่กับอย่างน้อย 800,000 UPDATEs รวมทั้ง EXEC 200 หรือ 800k EXECs (ขึ้นอยู่กับความถี่ในการดำเนินการตัวกรองที่จัดเก็บจริง)
ปัญหาของการเชื่อมต่อมากเกินไปนี้ได้รับการลดลงโดยอัตโนมัติตามคำแนะนำที่ฉันระบุไว้ข้างต้น ด้วยการสร้างธุรกรรมและทำ UPDATE ทั้งหมดภายในธุรกรรมนั้นคุณจะต้องเปิดการเชื่อมต่อนั้นและนำมาใช้ซ้ำสำหรับแต่ละUPDATE
รายการ ไม่ว่าการเชื่อมต่อจะถูกเปิดไว้จากการโทรเริ่มต้นเพื่อรับ 4000 แถวต่อการดำเนินการที่ระบุFileID
หรือปิดหลังจากนั้นการดำเนินการ "รับ" และเปิดอีกครั้งสำหรับการอัปเดตนั้นส่งผลกระทบน้อยกว่าเพราะตอนนี้เรากำลังพูดถึงความแตกต่าง 200 หรือ 400 การเชื่อมต่อทั้งหมดในกระบวนการทั้งหมด
อัปเดต 3:
ฉันทำการทดสอบอย่างรวดเร็ว โปรดทราบว่านี่เป็นการทดสอบในระดับที่ค่อนข้างเล็กและไม่ใช่การดำเนินการเดียวกันที่แน่นอน (INSERT บริสุทธิ์กับ EXEC + UPDATE) อย่างไรก็ตามความแตกต่างในเวลาที่เกี่ยวข้องกับวิธีการจัดการการเชื่อมต่อและการทำธุรกรรมยังคงมีความเกี่ยวข้องดังนั้นข้อมูลสามารถคาดการณ์ว่าจะมีผลกระทบที่ค่อนข้างคล้ายกันที่นี่
พารามิเตอร์การทดสอบ:
- SQL Server 2012 Developer Edition (64- บิต), SP2
โต๊ะ:
CREATE TABLE dbo.ManyInserts
(
RowID INT NOT NULL IDENTITY(1, 1) PRIMARY KEY,
InsertTime DATETIME NOT NULL DEFAULT (GETDATE()),
SomeValue BIGINT NULL
);
การดำเนินงาน:
INSERT INTO dbo.ManyInserts (SomeValue) VALUES ({LoopIndex * 12});
- เม็ดมีดรวมต่อการทดสอบแต่ละครั้ง: 10,000
- รีเซ็ตต่อการทดสอบแต่ละครั้ง:
TRUNCATE TABLE dbo.ManyInserts;
(ตามลักษณะของการทดสอบนี้การทำ FREEPROCCACHE, FREESYSTEMCACHE และ DROPCLEANBUFFERS นั้นไม่ได้เพิ่มมูลค่ามากนัก)
- รุ่นการกู้คืน: SIMPLE (และอาจจะ 1 GB ฟรีในล็อกไฟล์)
- การทดสอบที่ใช้ธุรกรรมใช้การเชื่อมต่อเดียวโดยไม่คำนึงถึงจำนวนธุรกรรม
ผล:
Test Milliseconds
------- ------------
10k INSERTs across 10k Connections 3968 - 4163
10k INSERTs across 1 Connection 3466 - 3654
10k INSERTs across 1 Transaction 1074 - 1086
10k INSERTs across 10 Transactions 1095 - 1169
อย่างที่คุณเห็นแม้ว่าการเชื่อมต่อ ADO กับฐานข้อมูลจะถูกใช้ร่วมกันระหว่างการดำเนินการทั้งหมดแล้วจัดกลุ่มให้เป็นแบทช์โดยใช้ทรานแซคชันที่ชัดเจน (วัตถุ ADO จะสามารถจัดการกับสิ่งนี้ได้) รับประกันอย่างมีนัยสำคัญ ลดเวลากระบวนการโดยรวม