ฉันเขียนแอปพลิเคชันด้วยแบ็กเอนด์ SQL Server ที่รวบรวมและจัดเก็บและบันทึกจำนวนมาก ฉันได้คำนวณว่าที่จุดสูงสุดปริมาณการบันทึกโดยเฉลี่ยอยู่ที่ประมาณ 3-4 พันล้านต่อวัน (การดำเนินการ 20 ชั่วโมง)
โซลูชันดั้งเดิมของฉัน (ก่อนที่ฉันจะทำการคำนวณข้อมูลจริง) เพื่อให้แอปพลิเคชันของฉันแทรกระเบียนลงในตารางเดียวกันที่ลูกค้าของฉันสอบถาม นั่นขัดข้องและถูกเผาอย่างรวดเร็วค่อนข้างชัดเจนเพราะเป็นไปไม่ได้ที่จะสืบค้นตารางที่มีการแทรกเรคคอร์ดจำนวนมาก
โซลูชันที่สองของฉันคือการใช้ฐานข้อมูล 2 ฐานข้อมูลหนึ่งสำหรับข้อมูลที่ได้รับจากแอปพลิเคชันและอีกหนึ่งฐานข้อมูลสำหรับไคลเอ็นต์ที่พร้อมใช้งาน
แอปพลิเคชันของฉันจะรับข้อมูลและแบ่งเป็นชุด ~ 100k เรคคอร์ดและแทรกจำนวนมากในตารางการจัดเตรียม หลังจาก ~ 100k บันทึกแอปพลิเคชันทันทีสร้างตารางการแสดงละครอีกด้วยสคีมาเหมือนก่อนหน้าและเริ่มแทรกลงในตารางนั้น มันจะสร้างเรกคอร์ดในตารางงานที่มีชื่อของตารางที่มีเรกคอร์ด 100k และกระบวนงานที่เก็บไว้ในฝั่ง SQL Server จะย้ายข้อมูลจากตาราง staging ไปยังตารางการผลิตที่ไคลเอ็นต์พร้อมแล้วปล่อย ตารางตารางชั่วคราวที่สร้างโดยแอปพลิเคชันของฉัน
ฐานข้อมูลทั้งสองมีชุดตาราง 5 ชุดที่มีสคีมาเดียวกันยกเว้นฐานข้อมูลที่มีตารางงาน ฐานข้อมูลการจัดเตรียมไม่มีข้อ จำกัด ด้านความสมบูรณ์, คีย์, ดัชนี ฯลฯ ... บนตารางที่มีระเบียนจำนวนมากที่จะอยู่ SignalValues_staging
ดังต่อไปนี้ชื่อตารางเป็น เป้าหมายคือเพื่อให้แอปพลิเคชันของฉันกระแทกข้อมูลลงใน SQL Server โดยเร็วที่สุด เวิร์กโฟลว์ของการสร้างตารางได้ทันทีเพื่อให้สามารถโยกย้ายได้อย่างง่ายดายทำงานได้ค่อนข้างดี
ต่อไปนี้คือ 5 ตารางที่เกี่ยวข้องจากฐานข้อมูลการจัดเตรียมของฉันรวมถึงตารางงานของฉัน:
ขั้นตอนการจัดเก็บที่ฉันได้เขียนจัดการการเคลื่อนย้ายข้อมูลจากตาราง staging ทั้งหมดและแทรกลงในการผลิต ด้านล่างเป็นส่วนหนึ่งของขั้นตอนการจัดเก็บของฉันที่แทรกลงในการผลิตจากตารางการแสดงละคร:
-- Signalvalues jobs table.
SELECT *
,ROW_NUMBER() OVER (ORDER BY JobId) AS 'RowIndex'
INTO #JobsToProcess
FROM
(
SELECT JobId
,ProcessingComplete
,SignalValueStagingTableName AS 'TableName'
,(DATEDIFF(SECOND, (SELECT last_user_update
FROM sys.dm_db_index_usage_stats
WHERE database_id = DB_ID(DB_NAME())
AND OBJECT_ID = OBJECT_ID(SignalValueStagingTableName))
,GETUTCDATE())) SecondsSinceLastUpdate
FROM SignalValueJobs
) cte
WHERE cte.ProcessingComplete = 1
OR cte.SecondsSinceLastUpdate >= 120
DECLARE @i INT = (SELECT COUNT(*) FROM #JobsToProcess)
DECLARE @jobParam UNIQUEIDENTIFIER
DECLARE @currentTable NVARCHAR(128)
DECLARE @processingParam BIT
DECLARE @sqlStatement NVARCHAR(2048)
DECLARE @paramDefinitions NVARCHAR(500) = N'@currentJob UNIQUEIDENTIFIER, @processingComplete BIT'
DECLARE @qualifiedTableName NVARCHAR(128)
WHILE @i > 0
BEGIN
SELECT @jobParam = JobId, @currentTable = TableName, @processingParam = ProcessingComplete
FROM #JobsToProcess
WHERE RowIndex = @i
SET @qualifiedTableName = '[Database_Staging].[dbo].['+@currentTable+']'
SET @sqlStatement = N'
--Signal values staging table.
SELECT svs.* INTO #sValues
FROM '+ @qualifiedTableName +' svs
INNER JOIN SignalMetaData smd
ON smd.SignalId = svs.SignalId
INSERT INTO SignalValues SELECT * FROM #sValues
SELECT DISTINCT SignalId INTO #uniqueIdentifiers FROM #sValues
DELETE c FROM '+ @qualifiedTableName +' c INNER JOIN #uniqueIdentifiers u ON c.SignalId = u.SignalId
DROP TABLE #sValues
DROP TABLE #uniqueIdentifiers
IF NOT EXISTS (SELECT TOP 1 1 FROM '+ @qualifiedTableName +') --table is empty
BEGIN
-- processing is completed so drop the table and remvoe the entry
IF @processingComplete = 1
BEGIN
DELETE FROM SignalValueJobs WHERE JobId = @currentJob
IF '''+@currentTable+''' <> ''SignalValues_staging''
BEGIN
DROP TABLE '+ @qualifiedTableName +'
END
END
END
'
EXEC sp_executesql @sqlStatement, @paramDefinitions, @currentJob = @jobParam, @processingComplete = @processingParam;
SET @i = @i - 1
END
DROP TABLE #JobsToProcess
ฉันใช้sp_executesql
เพราะชื่อตารางสำหรับตารางการจัดเตรียมมาเป็นข้อความจากระเบียนในตารางงาน
โพรซีเดอร์ที่เก็บนี้รันทุก 2 วินาทีโดยใช้กลอุบายที่ฉันเรียนรู้จากโพสต์ dba.stackexchange.comนี้
ปัญหาที่ฉันแก้ไขไม่ได้สำหรับชีวิตของฉันคือความเร็วที่เม็ดมีดเข้าสู่การผลิตถูกกระทำ แอปพลิเคชันของฉันสร้างตารางชั่วคราวและเติมข้อมูลด้วยระเบียนอย่างรวดเร็วอย่างไม่น่าเชื่อ การแทรกการผลิตไม่สามารถติดตามจำนวนของตารางและในที่สุดก็มีส่วนเกินของตารางเป็นพัน เพียงวิธีที่ฉันเคยสามารถที่จะให้ทันกับข้อมูลที่เข้ามาคือการลบคีย์ทั้งหมดดัชนี จำกัด ฯลฯ ... ในการผลิตSignalValues
ตาราง ปัญหาที่ฉันเผชิญคือตารางท้ายสุดมีระเบียนจำนวนมากที่ไม่สามารถสืบค้นได้
ฉันพยายามแบ่งตารางโดยใช้[Timestamp]
คอลัมน์เป็นพาร์ทิชันที่ไม่มีประโยชน์ การทำดัชนีรูปแบบใด ๆ จะทำให้เม็ดมีดช้าลงจนไม่สามารถติดตามได้ นอกจากนี้ฉันต้องสร้างหลายพันพาร์ติชัน (หนึ่งทุก ๆ นาทีหรือไม่?) ปีล่วงหน้า ฉันไม่สามารถหาวิธีสร้างมันได้ในทันที
ฉันพยายามสร้างการแบ่งพาร์ติชันโดยเพิ่มคอลัมน์จากการคำนวณลงในตารางที่เรียกว่าTimestampMinute
มีค่าอยู่, บนINSERT
, DATEPART(MINUTE, GETUTCDATE())
. ยังช้าเกินไป
ฉันได้พยายามทำให้มันเป็นตารางหน่วยความจำเพิ่มประสิทธิภาพตามบทความนี้ไมโครซอฟท์ บางทีฉันอาจไม่เข้าใจวิธีการใช้งาน แต่ MOT ทำให้เม็ดมีดช้าลงอย่างใด
ฉันได้ตรวจสอบแผนการดำเนินการของขั้นตอนการจัดเก็บแล้วและพบว่า (ฉันคิดว่า?) การดำเนินการที่เข้มข้นที่สุดคือ
SELECT svs.* INTO #sValues
FROM '+ @qualifiedTableName +' svs
INNER JOIN SignalMetaData smd
ON smd.SignalId = svs.SignalId
สำหรับฉันมันไม่สมเหตุสมผล: ฉันได้เพิ่มการบันทึกนาฬิกาแขวนในขั้นตอนการจัดเก็บที่พิสูจน์เป็นอย่างอื่น
ในแง่ของการบันทึกเวลาคำสั่งนั้นข้างต้นจะดำเนินการใน ~ 300ms ในบันทึก 100k
คำสั่ง
INSERT INTO SignalValues SELECT * FROM #sValues
รันใน 2500-3000ms บนบันทึก 100k การลบระเบียนที่ได้รับผลกระทบจากตารางต่อ:
DELETE c FROM '+ @qualifiedTableName +' c INNER JOIN #uniqueIdentifiers u ON c.SignalId = u.SignalId
ใช้เวลาอีก 300 มิลลิวินาที
ฉันจะทำให้เร็วขึ้นได้อย่างไร SQL Server สามารถจัดการกับบันทึกเป็นพันล้านรายการต่อวันได้หรือไม่?
หากเกี่ยวข้องกันนี่คือ SQL Server 2014 Enterprise x64
การกำหนดค่าฮาร์ดแวร์:
ฉันลืมที่จะรวมฮาร์ดแวร์ในรอบแรกของคำถามนี้ ความผิดฉันเอง.
ฉันจะคำนำนี้ด้วยคำสั่งเหล่านี้: ฉันรู้ว่าฉันสูญเสียประสิทธิภาพเนื่องจากการกำหนดค่าฮาร์ดแวร์ของฉัน ฉันลองมาหลายครั้งแล้ว แต่ด้วยงบประมาณ C-Level การจัดตำแหน่งของดาวเคราะห์ ฯลฯ ... ไม่มีอะไรที่ฉันจะทำได้เพื่อให้การติดตั้งดีขึ้นอย่างน่าเสียดาย เซิร์ฟเวอร์กำลังทำงานบนเครื่องเสมือนและฉันไม่สามารถเพิ่มหน่วยความจำได้เพราะเราไม่มีอีกต่อไป
นี่คือข้อมูลระบบของฉัน:
ที่เก็บข้อมูลเชื่อมต่อกับเซิร์ฟเวอร์ VM ผ่านทางอินเทอร์เฟซ iSCSI ไปยังกล่อง NAS (ซึ่งจะลดประสิทธิภาพการทำงาน) กล่อง NAS มี 4 ไดรฟ์ในการกำหนดค่า RAID 10 พวกเขากำลัง 4TB WD WD4000FYYZ ดิสก์ไดรฟ์หมุนด้วยอินเตอร์เฟซ SATA 6GB / s เซิร์ฟเวอร์มีที่เก็บข้อมูลเพียงแห่งเดียวที่กำหนดค่าไว้ดังนั้น tempdb และฐานข้อมูลของฉันอยู่ในที่เก็บข้อมูลเดียวกัน
ค่าสูงสุด DOP เป็นศูนย์ ฉันควรเปลี่ยนสิ่งนี้เป็นค่าคงที่หรือปล่อยให้ SQL Server จัดการมันได้หรือไม่ ฉันอ่าน RCSI: ฉันถูกต้องแล้วหรือไม่ถ้าฉันได้รับประโยชน์อย่างเดียวจาก RCSI ที่มาพร้อมกับการอัปเดตแถว จะไม่มีการปรับปรุงใด ๆ ของระเบียนเหล่านี้โดยเฉพาะพวกเขาจะINSERT
เอ็ดและSELECT
เอ็ด RCSI จะยังให้ประโยชน์กับฉันหรือไม่
tempdb ของฉันคือ 8mb จากคำตอบด้านล่างจาก jyao ฉันเปลี่ยน #sValues เป็นตารางปกติเพื่อหลีกเลี่ยง tempdb โดยสิ้นเชิง ประสิทธิภาพเป็นเรื่องเดียวกัน ฉันจะพยายามเพิ่มขนาดและการเติบโตของ tempdb แต่เนื่องจากขนาดของ #sValues จะมากขึ้นหรือน้อยลงเป็นขนาดเดียวกับที่ฉันไม่คาดหวังว่าจะได้รับมาก
ฉันมีแผนการดำเนินการที่ฉันแนบไว้ด้านล่าง แผนการดำเนินการนี้คือการวนซ้ำหนึ่งตาราง staging - ระเบียน 100k การดำเนินการของแบบสอบถามนั้นค่อนข้างรวดเร็วประมาณ 2 วินาที แต่โปรดจำไว้ว่านี่เป็นดัชนีที่ไม่มีดัชนีในSignalValues
ตารางและSignalValues
เป้าหมายของดัชนีINSERT
นั้นไม่มีบันทึกอยู่ในนั้น