SQL:如果不是CPU或IO,什么会减慢INSERT的速度?


19

我们拥有大量写产品的数据库。我们刚刚购买了带有SSD的新服务器,以提供帮助。令我们惊讶的是,插入速度并不比存储速度慢得多的旧机器快。在基准测试期间,我们注意到SQL Server进程显示的IO速率非常低。

例如,我运行了在此页面上找到的脚本,除了我在循环周围添加了BEGIN TRAN和COMMIT之外。充其量我可以看到磁盘使用率达到7Mb / s,而CPU几乎没有达到5%。该服务器已安装64Gb并正在使用10。总运行时间为首次呼叫2分钟15秒,而后续呼叫大约1分钟。数据库正在简单恢复中,并且在测试期间处于空闲状态。我在每次通话之间都放了桌子。

为什么这么简单的脚本这么慢?几乎根本不使用硬件。专用的磁盘基准测试工具和SQLIO均表明SSD可以正常运行,并且读写速度高达500Mb / s。我知道随机写入比顺序写入要慢,但是我希望像这样的简单插入到没有聚簇索引的表中会更快。

最终,我们的情况要复杂得多,但是我觉得我需要先了解一个简单的案例。简而言之,我们的应用程序删除旧数据,然后使用SqlBulkCopy将新数据复制到临时表中,执行一些过滤,最后根据情况使用MERGE和/或INSERT INTO将数据复制到最终表中。

->编辑1:我遵循了马丁·史密斯(Martin Smith)链接的过程,得到以下结果:

[Wait Type]  [Wait Count] [Total Wait (ms)] [T. Resource Wait (ms)] [T. Signal Wait (ms)]
NETWORK_IO          5008              46735                 46587        148
LOGBUFFER           901               5994                  5977         17
PAGELATCH_UP        40                866                   865          1
SOS_SCHEDULER_YIELD 53279             219                   121          98
WRITELOG            5                 145                   145          0
PAGEIOLATCH_UP      4                 58                    58           0
LATCH_SH            5                 0                     0            0

考虑到没有要显示的结果,也没有数据可以传输到SQL文件以外的任何地方,我发现NETWORK_IO花费的时间很奇怪。NETWORK_IO类型是否包含所有IO?

->编辑2:我创建了20Gb RAM磁盘,并从那里安装了数据库。我在SSD上的最佳时间是48秒,而RAM磁盘的时间减少到了37秒。NETWORK_IO仍然是最大的等待。对RAM磁盘的最大写入速度约为250Mb / s,而它每秒可以处理几千兆字节。它仍然没有使用太多的CPU,那么什么阻碍了SQL?



3
NETWORK_IO可能是从3万美元的“1行(S)的影响”的消息被送回。您是否尝试添加SET NOCOUNT ON到脚本?
马丁·史密斯

是的,我添加了NOCOUNT。
2012年

2
奇怪。那时我对网络活动的期望并不高。您是否在两次运行之间删除了旧的扩展事件文件?读取它们的脚本使用通配符,EE_WaitStats*.xel因此旧的脚本将污染您的结果。
马丁·史密斯

好的,我明天将更新结果。
2012年

Answers:


9

我知道这是一个古老的问题,但这可能仍然对搜索者有帮助,并且这个问题时不时地出现。

之所以达到性能极限而没有看到任何资源瓶颈的主要原因是因为您已经达到了在一个会话单线程中可以处理的内容的极限。循环不是并行处理的,但是所有插入都是串行完成的。

就我而言,插入300万行需要36秒。这意味着每行36/30000000 = 0.000012秒。那太快了。在我的系统上,只需花费0.000012即可完成所有必要步骤。

使它更快完成的唯一方法是并行启动第二个会话。

如果我同时开始2次会话,那么两次插入都会进行1500万次插入。他们两个都在18秒内完成。我可以进行更多扩展,但是我的当前测试设置是通过两个并行会话达到95%的cpu,因此执行3会使结果不正确,因为我遇到了CPU瓶颈。

如果我同时启动2个并行会话并插入300万行,它们都将在39秒内完成。因此现在在39秒内达到了600万行。

好的,这仍然让我们等待NETWORK_IO的出现。

由于您正在使用扩展事件来跟踪它们,因此添加了NETWORK_IO等待。在我的情况下,插入内容需要36秒(平均)。当使用扩展事件方式时(从上面的链接的第一个评论中),这是注册的内容:

Wait Type             Wait Count  Total Wait Time (ms) Total Resource Wait Time (ms) Total Signal Wait Time (ms)
NETWORK_IO            3455        68808                68802                         6
PAGEIOLATCH_SH        3           64                   64                            0
PAGEIOLATCH_UP        12          58                   58                            0
WRITE_COMPLETION      8           15                   15                            0
WRITELOG              3           9                    9                             0
PAGELATCH_UP          2           4                    4                             0
SOS_SCHEDULER_YIELD   32277       1                    0                             1
IO_COMPLETION         8           0                    0                             0
LATCH_SH              3           0                    0                             0
LOGBUFFER             1           0                    0                             0

您可以看到NETWORK_IO的注册时间为68秒。但是由于insert循环是一个单线程操作,耗时36秒,所以不可能。(是的,使用了多个线程,但是操作是串行的,从不并行执行,因此您累积的等待时间不会超过查询的总持续时间)

如果我不使用扩展事件,而只是在一个安静的实例上使用wait stats DMV(仅由我运行插入操作),则会得到以下信息:

Wait Type                   Wait Count  Total Wait Time (ms)  Total Resource Wait Time (ms) Signal Resource Wait Time (ms)
SOS_SCHEDULER_YIELD             8873                 0.21                                    0.01                                    0.20
PAGEIOLATCH_UP                  3                    0.02                                    0.02                                    0.00
PREEMPTIVE_OS_AUTHENTICATIONOPS 17                   0.02                                    0.02                                    0.00
PAGEIOLATCH_SH                  1                    0.00                                    0.00                                    0.00

因此,您在扩展事件日志中看到的NETWORK_IO与您的插入循环无关。(如果您不开启nocount,则将有大量异步网络IO等待,+ 1 Martin)

但是我不知道为什么NETWORK_IO出现在扩展事件跟踪中。确保将事件写到异步文件目标上会累积ASYNC_NETWORK_IO,但可以肯定的是,所有这些操作都是在一个不同的SPID上完成的,然后在我们要过滤的SPID上完成。我可能会自己问这个新问题)


1
“达到性能极限而没有看到任何资源瓶颈是因为您已经达到了在一个会话单线程内可以处理的极限”:您正在描述100%CPU瓶颈(在一个内核上)。如果没有瓶颈,那么系统将运行得更快,因此必须采取其他措施。
Remus Rusanu 2012年

您的回答非常有帮助,爱德华。看起来并行性确实是解决我们已经在解决这个问题的方法,尽管它需要更改数据库布局。但是,像Remus一样,我仍然很好奇为什么机器似乎没有使用所有(一个)CPU或磁盘资源。
2012年

9

通常,您首先查看sys.dm_exec_requests,特别是查看wait_timewait_type然后wait_resource查看您的INSERT请求。这将清楚地表明什么阻止了您的INSERT。结果将指示是否是锁争用,文件增长事件,日志刷新等待,分配争用(作为PFS页面闩锁争用的清单)等。一旦测量,就相应地更新问题。我强烈敦促您立即停止并阅读“ 等待和队列”故障排除方法,然后再继续。


3

我在循环中与BEGIN TRAN / COMMIT链接的OP中的页面上运行了测试脚本。在我的机器上,第一次完成需要1:28。

然后,我将这两个命令移出了循环:

SELECT @Random = ROUND(((@Upper - @Lower -1) * RAND() + @Lower), 0)
SET @InsertDate = DATEADD(dd, @Random, GETDATE())

此后28秒内完成。

我不确定发生了什么,但是我猜测代码中可能存在某种睡眠RAND(),也许这是他们用于生成熵(更好的随机数)的算法的一部分。

FWIW,SSD并非始终是写繁重应用程序的最佳技术。为了获得最佳性能,请确保数据库日志与数据库数据位于不同的驱动器盘符中,并且日志文件已预先增长到最大大小,并且切勿截断日志。


感谢您的输入RickNZ。通过将代码移出循环,我没有得到更快的结果。等一下,我确实观察到,如果多次运行它,它将变得更快,这可能就是您所经历的。我知道SSD并不是灵丹妙药,但我仍然觉得性能不是它可以达到的。
德约夫,2012年


0

我正在检查sql 2008的等待事件列表,但没有看到列出的NETWORK_IO:http ://technet.microsoft.com/zh-cn/library/ms179984(v=sql.100) .aspx

我以为NETWORK_IO现在被列为ASYNC_NETWORK_IO,所以我想问一下您是否可以再次检查您的SQL版本,因为我很好奇该版本的等待事件如何/为什么出现。

至于根本没有出现网络等待,是的,即使在独立服务器上工作也可能发生。您是否检查过网卡的设置?我想知道它们是否是一个问题。

归根结底,可能只有几个资源瓶颈:内存,CPU,磁盘I / O,网络和锁定。您已经表明CPU和I / O并不是问题,并且您有NETWORK_IO的等待事件,因此建议您首先查看那些NIC卡。


1
NETWORK_IO之所以显示,是因为OP正在使用扩展事件。它从来没有更新过sys.dm_xe_map_values
Martin Smith

我在想同一个SQLRockstar,可能会发生什么。我确实尝试完全禁用网卡。马丁指出,某些旧文件可能仍然存在,我将更新结果以查看是否更改了任何内容。
2012年

同样,如果我们可以看到语句的执行计划,则可能会有所帮助。
SQLRockstar
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.