我已经编写了一个带有SQL Server后端的应用程序,该应用程序可以收集和存储大量记录。我已经计算出,在高峰时,平均记录量约为每天3亿4千万(运行20小时)。
我的原始解决方案(在完成数据的实际计算之前)是让我的应用程序将记录插入到客户查询的同一张表中。显然,该崩溃和烧毁很快,因为不可能查询插入了这么多记录的表。
我的第二个解决方案是使用2个数据库,一个用于应用程序接收的数据,另一个用于客户端就绪的数据。
我的应用程序将接收数据,将其分块成约10万条记录,然后批量插入到临时表中。在记录约100k之后,应用程序将即时创建另一个具有与以前相同的架构的登台表,然后开始插入该表中。它将在作业表中创建一条记录,该表的名称具有100k条记录,并且SQL Server端的存储过程会将数据从登台表移至可用于客户端的生产表,然后删除表由我的应用程序创建的临时表。
除了具有Jobs表的登台数据库之外,两个数据库都具有相同模式的5个表的相同集合。暂存数据库在将要存储大量记录的表上没有完整性约束,键,索引等。如下所示,表名称为SignalValues_staging
。目的是让我的应用程序尽快将数据装入SQL Server。动态创建表以便轻松迁移表的工作流程效果很好。
以下是我的登台数据库中的5个相关表以及我的作业表:
我编写的存储过程负责处理所有登台表中的数据并将其插入生产环境。以下是我的存储过程的一部分,该过程从登台表插入生产环境:
-- 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
这是因为临时表的表名来自作业表中记录的文本。
使用我从dba.stackexchange.com帖子中学到的技巧,此存储过程每2秒运行一次。
我一生无法解决的问题是插入生产的速度。我的应用程序创建了临时登台表,并以令人难以置信的快速填充记录。插入产品无法跟上表格的数量,最终有成千上万的表格过剩。我能够跟上传入数据的唯一方法是删除生产SignalValues
表上的所有键,索引,约束等。然后我要面对的问题是,该表最终有太多记录,因此无法查询。
我尝试使用[Timestamp]
作为分区列对表进行分区无济于事。任何形式的索引都会使插入速度变慢,以至于无法跟上。另外,我需要提前创建数千个分区(每分钟一个小时?一个小时)。我不知道如何动态创建它们
我试图创建通过添加一个计算列称为表分区TimestampMinute
,其价值是,上INSERT
,DATEPART(MINUTE, GETUTCDATE())
。还是太慢了。
我已尝试根据此Microsoft文章使其成为内存优化表。也许我不知道该怎么做,但是MOT使刀片变慢了。
我检查了存储过程的执行计划,发现(我认为?)最密集的操作是
SELECT svs.* INTO #sValues
FROM '+ @qualifiedTableName +' svs
INNER JOIN SignalMetaData smd
ON smd.SignalId = svs.SignalId
在我看来,这没有任何意义:我向存储过程添加了挂钟记录,事实证明并非如此。
在时间记录方面,上述特定语句在100k条记录上的执行时间约为300毫秒。
该声明
INSERT INTO SignalValues SELECT * FROM #sValues
在100,000个记录上以2500-3000ms执行。从表中删除受影响的记录,具体如下:
DELETE c FROM '+ @qualifiedTableName +' c INNER JOIN #uniqueIdentifiers u ON c.SignalId = u.SignalId
再花300毫秒。
我怎样才能使它更快?SQL Server每天可以处理数十亿条记录吗?
如果相关,则为SQL Server 2014 Enterprise x64。
硬件配置:
我忘记在此问题的第一遍中包括硬件。我的错。
我将使用以下语句作为开头:我知道由于硬件配置,我将失去一些性能。我已经尝试了很多次,但是由于预算,C级,行星的对准等原因,不幸的是我无能为力。该服务器正在虚拟机上运行,我什至无法增加内存,因为我们根本没有更多的内存了。
这是我的系统信息:
该存储通过iSCSI接口连接到VM服务器,并连接到NAS盒(这会降低性能)。NAS盒在RAID 10配置中有4个驱动器。它们是具有6GB / s SATA接口的4TB WD WD WD4000FYYZ旋转磁盘驱动器。该服务器仅配置了一个数据存储,因此tempdb和我的数据库位于同一数据存储上。
最大DOP为零。我应该将其更改为恒定值还是让SQL Server处理?我读了RCSI:我是否正确假设RCSI的唯一好处是行更新?永远不会有更新任何这些特定记录,他们将INSERT
ED和SELECT
主编。RCSI还会使我受益吗?
我的tempdb是8mb。根据jyao的以下回答,我将#sValues更改为常规表,以完全避免使用tempdb。性能虽然差不多。我将尝试增加tempdb的大小和增长,但是鉴于#sValues的大小或多或少总是相同的,所以我预计不会有太大的收获。
我已经制定了以下所附的执行计划。该执行计划是登台表的一个迭代-100k条记录。查询的执行速度相当快,大约需要2秒钟,但是请记住,这在SignalValues
表上没有索引,并且该SignalValues
表(作为的目标)中INSERT
没有记录。