INSERT INTO在SQL Server 2012中随着时间的推移变得慢得多

Ph0*_*n1x 7 sql database sql-server sql-server-2008 database-performance

我们有一个非常大的数据库WriteDB,存储原始交易数据,我们使用此表来快速写入.然后使用sql脚本我将数据导入WriteDBReadDB相对相同的表中,但扩展了一些额外的值+关系添加.导入脚本是这样的:

TRUNCATE TABLE [ReadDB].[dbo].[Price]
GO
INSERT INTO [ReadDB].[dbo].[Price]
SELECT a.*, 0 as ValueUSD, 0 as ValueEUR
from [WriteDB].[dbo].[Price] a
JOIN [ReadDB].[dbo].[Companies] b ON a.QuoteId = b.QuoteID
Run Code Online (Sandbox Code Playgroud)

所以最初有大约130万.此表中的行(~50GB).每天他们中的一些人都会添加,其中一些会发生变化,所以现在我们决定不会复杂化逻辑并重新导入所有数据.出于某种原因,由于某种原因,这个脚本的工作时间越来越长,几乎相同数量的数据.首先运行需要~1h,现在已经花了3h

另外SQL Server导入后工作也不顺利.导入(或在其中)如果我尝试运行不同的查询,即使是最简单的,它们通常也会因超时错误而失败.

这种不良行为的原因是什么以及如何解决这个问题?

Mic*_*ito 5

一种理论是,您的第一个50GB数据集已填充可用于缓存的内存.截断表后,您的缓存现在实际上是空的.这种交替行为使得高速缓存的有效使用变得困难并且导致大量的高速缓存未命中/增加的IO时间.

考虑以下事件序列:

  1. 您将初始数据集加载到WriteDb中.在加载操作期间,WriteDb中的页面被缓存.内存争用非常少,因为只有一个数据集副本和足够的内存.
  2. 您最初填充ReadDb.填充ReadDb所需的页面(WriteDb中的数据)已经在很大程度上被缓存.磁盘需要的读取次数较少,您的IO时间可专用于为ReadDb写入插入的数据.(这是你的第一次快速运行.)
  3. 您将第二个数据集加载到WriteDb中.在加载操作期间,没有足够的内存来缓存ReadDb中的现有数据和写入WriteDb的新数据.这种内存争用导致WriteDb缓存的页面更少.
  4. 你截断了ReadDb.这会使大部分缓存失效(即缓存的50GB ReadDb数据).
  5. 然后,您尝试第二次加载ReadDb.在这里,您只有很少的WriteDb缓存,因此您的IO时间在读取WriteDb页面(您的查询)和写入ReadDb页面(您的插入)之间分配.(这是你的第二次慢跑.)

您可以通过在第一次和第二次加载操作期间比较SQL Server缓存未命中率来测试此理论.

一些提高性能的方法可能是:

  • 为ReadDb/WriteDb使用单独的磁盘阵列以提高并行IO性能.
  • 增加可用缓存(服务器内存量)以适应ReadDb + WriteDb的总大小并最大限度地减少缓存未命中.
  • 通过使用MERGE语句而不是一次转储/加载50GB数据,最大限度地减少每个加载操作对现有缓存页面的影响.