从 Sql Server 2008 升级到 Sql Server 2016 之后,以前很快的存储过程现在变慢了
After upgrading from Sql Server 2008 to Sql Server 2016 a stored procedure that was fast is now slow
我们有一个存储过程 returns 属于地理空间区域 ("geography") 的所有记录。它使用 CTE(带有)、一些联合、一些内部连接和 returns 数据作为 XML;没有争议或前沿,但也不是微不足道的。
此存储过程在 SQL Server 2008 上为我们服务多年。在相对较慢的服务器上 运行 不到 1 秒。我们刚刚迁移到 SQL Server 2016,这是一台具有大量内存和超快 SDD 的超快服务器。
整个数据库和相关应用程序在这台新服务器上非常快,我们对此非常满意。然而,这个存储过程 运行 在 16 秒而不是 1 秒内——针对完全相同的参数和完全相同的数据集。
我们已经更新了这个数据库的索引和统计数据。我们还将数据库的兼容级别从 100 更改为 130。
有趣的是,我重写了存储过程以使用临时 table 和 'insert' 而不是使用 CTE。这使时间从 16 秒减少到 4 秒。
执行计划没有提供任何关于瓶颈可能在哪里的明显见解。
我们有点卡壳了。接下来我们应该做什么?提前致谢。
--
我现在花在这个问题上的时间比我愿意承认的要多。我已将存储过程归结为以下查询来演示问题。
drop table #T
declare @viewport sys.geography=convert(sys.geography,0xE610000001041700000000CE08C22D7740C002370B7670F4624000CE08C22D7740C002378B5976F4624000CE08C22D7740C003370B3D7CF4624000CE08C22D7740C003378B2082F4624000CE08C22D7740C003370B0488F4624000CE08C22D7740C004378BE78DF4624000CE08C22D7740C004370BCB93F4624000CE08C22D7740C004378BAE99F4624000CE08C22D7740C005370B929FF4624000CE08C22D7740C005378B75A5F4624000CE08C22D7740C005370B59ABF462406F22B7698E7640C005370B59ABF462406F22B7698E7640C005378B75A5F462406F22B7698E7640C005370B929FF462406F22B7698E7640C004378BAE99F462406F22B7698E7640C004370BCB93F462406F22B7698E7640C004378BE78DF462406F22B7698E7640C003370B0488F462406F22B7698E7640C003378B2082F462406F22B7698E7640C003370B3D7CF462406F22B7698E7640C002378B5976F462406F22B7698E7640C002370B7670F4624000CE08C22D7740C002370B7670F4624001000000020000000001000000FFFFFFFF0000000003)
declare @outputControlParameter nvarchar(max) = 'a value passed in through a parameter to the stored that controls the nature of data to return. This is not the solution you are looking for'
create table #T
(value int)
insert into #T
select 136561 union
select 16482 -- These values are sourced from parameters into the stored proc
select
[GeoServices_Location].[GeographicServicesGatewayId],
[GeoServices_Location].[Coordinate].Lat,
[GeoServices_Location].[Coordinate].Long
from GeoServices_Location
inner join GeoServices_GeographicServicesGateway
on GeoServices_Location.GeographicServicesGatewayId = GeoServices_GeographicServicesGateway.GeographicServicesGatewayId
where
(
(len(@outputControlParameter) > 0 and GeoServices_Location.GeographicServicesGatewayId in (select value from #T))
or (len(@outputControlParameter) = 0 and GeoServices_Location.Coordinate.STIntersects(@viewport) = 1)
)
and GeoServices_GeographicServicesGateway.PrimarilyFoundOnLayerId IN (3,8,9,5)
GO
将存储过程归结为这个,它在 SQL Server 2008 上运行 0 秒,在 SQL Server 2016
上运行 5 秒
http://www.filedropper.com/newserver-slowexecutionplan
http://www.filedropper.com/oldserver-fastexecutionplan
Windows Server 2016 在 Geospatial Intersects 调用上令人窒息,94% 的时间都花在那里。 Sql Server 2008 正在花时间处理许多其他步骤,包括哈希匹配和并行以及其他标准内容。
记住这是同一个数据库。一个刚刚被复制到 SQL Server 2016 机器并提高了兼容性级别。
为了解决这个问题,我实际上重写了存储过程,以便 Sql Server 2016 不会阻塞。我在 250 毫秒内有 运行。然而,这本来不应该发生,我担心还有其他以前经过微调的查询或存储过程现在没有 运行 有效。
提前致谢。
--
此外,我建议添加跟踪标志 -T6534 来启动服务的参数。它对查询时间没有影响。我也尝试将选项(QUERYTRACEON 6534)添加到查询的末尾,但同样没有任何区别。
- 检查新服务器 (DB) 与旧服务器 (DB) 配置上 data/log 文件的增长情况:查询的数据库 运行 on + tempdb
- 检查日志是否有 I/O 个缓冲区错误
- 检查数据库的恢复模型 - 简单与 full/bulk
- 这是一贯的行为吗?也许一个进程在执行期间是运行?
- 关于 statistics/indexes - 您确定 运行 的数据样本正确吗? (看计划)
还有很多东西可以 checked/done - 但这个问题中没有足够的信息。
从您提供的查询计划中,我看到空间索引未在较新的服务器版本上使用。
使用空间索引提示确保查询优化器选择具有空间索引的计划:
select
[GeoServices_Location].[GeographicServicesGatewayId],
[GeoServices_Location].[Coordinate].Lat,
[GeoServices_Location].[Coordinate].Long
from GeoServices_Location with (index ([spatial_index_name]))...
我看到提示的问题是查询谓词中的 OR 操作,所以我对提示的建议实际上对这种情况没有帮助。
但是,我看到谓词依赖于@outputControlParameter,因此重写查询以将这两种情况分开可能会有所帮助(请参阅下面的建议)。
此外,从您的查询计划中我看到 SQL 2008 的查询计划是并行的,而 SQL 2016 的查询计划是串行的。使用选项(重新编译,querytraceon 8649)强制执行并行计划(如果你的新超高速服务器比旧服务器有更多的内核,应该会有帮助)。
if (len(@outputControlParameter) > 0)
select
[GeoServices_Location].[GeographicServicesGatewayId],
[GeoServices_Location].[Coordinate].Lat,
[GeoServices_Location].[Coordinate].Long
from GeoServices_Location
inner join GeoServices_GeographicServicesGateway
on GeoServices_Location.GeographicServicesGatewayId = GeoServices_GeographicServicesGateway.GeographicServicesGatewayId
where
GeoServices_Location.GeographicServicesGatewayId in (select value from #T))
and GeoServices_GeographicServicesGateway.PrimarilyFoundOnLayerId IN(3,8,9,5)
option (recompile, querytraceon 8649)
else
select
[GeoServices_Location].[GeographicServicesGatewayId],
[GeoServices_Location].[Coordinate].Lat,
[GeoServices_Location].[Coordinate].Long
from GeoServices_Location with (index ([SPATIAL_GeoServices_Location]))
inner join GeoServices_GeographicServicesGateway
on GeoServices_Location.GeographicServicesGatewayId = GeoServices_GeographicServicesGateway.GeographicServicesGatewayId
where
GeoServices_Location.Coordinate.STIntersects(@viewport) = 1
and GeoServices_GeographicServicesGateway.PrimarilyFoundOnLayerId IN (3,8,9,5)
option (recompile, querytraceon 8649)
我们有一个存储过程 returns 属于地理空间区域 ("geography") 的所有记录。它使用 CTE(带有)、一些联合、一些内部连接和 returns 数据作为 XML;没有争议或前沿,但也不是微不足道的。
此存储过程在 SQL Server 2008 上为我们服务多年。在相对较慢的服务器上 运行 不到 1 秒。我们刚刚迁移到 SQL Server 2016,这是一台具有大量内存和超快 SDD 的超快服务器。
整个数据库和相关应用程序在这台新服务器上非常快,我们对此非常满意。然而,这个存储过程 运行 在 16 秒而不是 1 秒内——针对完全相同的参数和完全相同的数据集。
我们已经更新了这个数据库的索引和统计数据。我们还将数据库的兼容级别从 100 更改为 130。
有趣的是,我重写了存储过程以使用临时 table 和 'insert' 而不是使用 CTE。这使时间从 16 秒减少到 4 秒。
执行计划没有提供任何关于瓶颈可能在哪里的明显见解。
我们有点卡壳了。接下来我们应该做什么?提前致谢。
--
我现在花在这个问题上的时间比我愿意承认的要多。我已将存储过程归结为以下查询来演示问题。
drop table #T
declare @viewport sys.geography=convert(sys.geography,0xE610000001041700000000CE08C22D7740C002370B7670F4624000CE08C22D7740C002378B5976F4624000CE08C22D7740C003370B3D7CF4624000CE08C22D7740C003378B2082F4624000CE08C22D7740C003370B0488F4624000CE08C22D7740C004378BE78DF4624000CE08C22D7740C004370BCB93F4624000CE08C22D7740C004378BAE99F4624000CE08C22D7740C005370B929FF4624000CE08C22D7740C005378B75A5F4624000CE08C22D7740C005370B59ABF462406F22B7698E7640C005370B59ABF462406F22B7698E7640C005378B75A5F462406F22B7698E7640C005370B929FF462406F22B7698E7640C004378BAE99F462406F22B7698E7640C004370BCB93F462406F22B7698E7640C004378BE78DF462406F22B7698E7640C003370B0488F462406F22B7698E7640C003378B2082F462406F22B7698E7640C003370B3D7CF462406F22B7698E7640C002378B5976F462406F22B7698E7640C002370B7670F4624000CE08C22D7740C002370B7670F4624001000000020000000001000000FFFFFFFF0000000003)
declare @outputControlParameter nvarchar(max) = 'a value passed in through a parameter to the stored that controls the nature of data to return. This is not the solution you are looking for'
create table #T
(value int)
insert into #T
select 136561 union
select 16482 -- These values are sourced from parameters into the stored proc
select
[GeoServices_Location].[GeographicServicesGatewayId],
[GeoServices_Location].[Coordinate].Lat,
[GeoServices_Location].[Coordinate].Long
from GeoServices_Location
inner join GeoServices_GeographicServicesGateway
on GeoServices_Location.GeographicServicesGatewayId = GeoServices_GeographicServicesGateway.GeographicServicesGatewayId
where
(
(len(@outputControlParameter) > 0 and GeoServices_Location.GeographicServicesGatewayId in (select value from #T))
or (len(@outputControlParameter) = 0 and GeoServices_Location.Coordinate.STIntersects(@viewport) = 1)
)
and GeoServices_GeographicServicesGateway.PrimarilyFoundOnLayerId IN (3,8,9,5)
GO
将存储过程归结为这个,它在 SQL Server 2008 上运行 0 秒,在 SQL Server 2016
上运行 5 秒http://www.filedropper.com/newserver-slowexecutionplan
http://www.filedropper.com/oldserver-fastexecutionplan
Windows Server 2016 在 Geospatial Intersects 调用上令人窒息,94% 的时间都花在那里。 Sql Server 2008 正在花时间处理许多其他步骤,包括哈希匹配和并行以及其他标准内容。
记住这是同一个数据库。一个刚刚被复制到 SQL Server 2016 机器并提高了兼容性级别。
为了解决这个问题,我实际上重写了存储过程,以便 Sql Server 2016 不会阻塞。我在 250 毫秒内有 运行。然而,这本来不应该发生,我担心还有其他以前经过微调的查询或存储过程现在没有 运行 有效。
提前致谢。
--
此外,我建议添加跟踪标志 -T6534 来启动服务的参数。它对查询时间没有影响。我也尝试将选项(QUERYTRACEON 6534)添加到查询的末尾,但同样没有任何区别。
- 检查新服务器 (DB) 与旧服务器 (DB) 配置上 data/log 文件的增长情况:查询的数据库 运行 on + tempdb
- 检查日志是否有 I/O 个缓冲区错误
- 检查数据库的恢复模型 - 简单与 full/bulk
- 这是一贯的行为吗?也许一个进程在执行期间是运行?
- 关于 statistics/indexes - 您确定 运行 的数据样本正确吗? (看计划)
还有很多东西可以 checked/done - 但这个问题中没有足够的信息。
从您提供的查询计划中,我看到空间索引未在较新的服务器版本上使用。 使用空间索引提示确保查询优化器选择具有空间索引的计划:
select
[GeoServices_Location].[GeographicServicesGatewayId],
[GeoServices_Location].[Coordinate].Lat,
[GeoServices_Location].[Coordinate].Long
from GeoServices_Location with (index ([spatial_index_name]))...
我看到提示的问题是查询谓词中的 OR 操作,所以我对提示的建议实际上对这种情况没有帮助。 但是,我看到谓词依赖于@outputControlParameter,因此重写查询以将这两种情况分开可能会有所帮助(请参阅下面的建议)。 此外,从您的查询计划中我看到 SQL 2008 的查询计划是并行的,而 SQL 2016 的查询计划是串行的。使用选项(重新编译,querytraceon 8649)强制执行并行计划(如果你的新超高速服务器比旧服务器有更多的内核,应该会有帮助)。
if (len(@outputControlParameter) > 0)
select
[GeoServices_Location].[GeographicServicesGatewayId],
[GeoServices_Location].[Coordinate].Lat,
[GeoServices_Location].[Coordinate].Long
from GeoServices_Location
inner join GeoServices_GeographicServicesGateway
on GeoServices_Location.GeographicServicesGatewayId = GeoServices_GeographicServicesGateway.GeographicServicesGatewayId
where
GeoServices_Location.GeographicServicesGatewayId in (select value from #T))
and GeoServices_GeographicServicesGateway.PrimarilyFoundOnLayerId IN(3,8,9,5)
option (recompile, querytraceon 8649)
else
select
[GeoServices_Location].[GeographicServicesGatewayId],
[GeoServices_Location].[Coordinate].Lat,
[GeoServices_Location].[Coordinate].Long
from GeoServices_Location with (index ([SPATIAL_GeoServices_Location]))
inner join GeoServices_GeographicServicesGateway
on GeoServices_Location.GeographicServicesGatewayId = GeoServices_GeographicServicesGateway.GeographicServicesGatewayId
where
GeoServices_Location.Coordinate.STIntersects(@viewport) = 1
and GeoServices_GeographicServicesGateway.PrimarilyFoundOnLayerId IN (3,8,9,5)
option (recompile, querytraceon 8649)