SQL Server 2016 Enterprise 性能不佳

Nel*_*pes 8 performance sql-server sql-server-2016 enterprise-edition

很抱歉说得太长了,但我想给你尽可能多的信息,这样可能对分析有所帮助。

我知道有几个帖子有类似的问题,但是,我已经关注了这些不同的帖子和网络上的其他信息,但问题仍然存在。

我在 SQL Server 中有一个严重的性能问题,这让用户发疯。这个问题拖了好几年,直到2016年底由另一个实体管理,从2017年开始由我管理。

在 2017 年年中,我能够通过遵循 Microsoft SQL Server 2012 性能仪表板报告指示的索引提示来解决问题。效果立竿见影,听起来很神奇。最后几天几乎总是在 100% 的处理器变得超级安静,用户的反馈是响亮的。甚至我们的 ERP 技术人员也很高兴,因为通常需要 20 分钟才能获得某些列表,而最终他可以在几秒钟内完成。

然而,随着时间的推移,它慢慢开始恶化。我避免创建更多的索引,因为担心太多的索引会降低性能。但在某些时候,我不得不删除那些没有用的,并创建 Performance Dashboard 向我建议的新的。但是没有影响。

缓慢的感觉主要是在 ERP 中进行保存和咨询时。

我有一个专用于 SQL Server 2016 Enterprise(64 位)的 Windows Server 2012 R2,配置如下:

  • CPU:英特尔至强 CPU E5-2650 v3 @ 2.30GHz
  • 内存:84 GB
  • 在存储方面,服务器有一个专用于操作系统的卷,另一个专用于数据,另一个专用于日志。
  • 17 个数据库
  • 用户:
    • 在最大的 DB 中连接或多或少 113 个用户并发
    • 在另一个大约有 9 个用户
    • 其中两个是 3 + 3
    • 其余各只有 1 个用户
    • 我们有一个也为更大的数据库编写的网络,但使用频率要低得多,应该有大约 20 个用户。
  • 数据库的大小:
    • 最大的数据库有 290 GB
    • 第二大有100GB
    • 第三大有 20 GB
    • 第四个 14 GB
    • 其余每个都刚刚超过 3 GB

这是生产实例,但我们也有一个开发实例,我认为出于此目的可以忽略它,因为大多数时候我是那里唯一的连接,但是这个问题不断发生,即使我没有连接.

处理器几乎总是这样:

在此处输入图片说明

我们有在夜间运行的例程(没有问题)和一些在白天运行。

用户通过远程桌面连接到由 ODBC 32 配置的其他计算机以访问 SQL Server。

服务器所在的数据中心有 100/100 Mbps,以及我所在的位置。大多数站点通过 MPLS 链接,其他站点通过 IPSec(从 FO 到 4G)链接。供应商做了很多分析,电路没问题。

缓存命中率为 99%(用户请求和用户会话)

等待看起来像这样:

在此处输入图片说明

我已经用 Perfmon 收集了数据,如果对您的分析有帮助,我就有了结果 - 就我个人而言,我没有从分析中得到任何结论。

我指望您支持解决此问题,并提供您认为解决此问题所必需的信息。

非常感谢。

这是 sp_blitz 降价(我用化名替换了公司名称):

优先级 1:可靠性

  • 最后一次良好的 DBCC CHECKDB 超过 2 周

    • 掌握
    • 模型 - 最后一次成功的 CHECKDB:2018-02-07 15:04:26.560

    • msdb - 最后一次成功的 CHECKDB:2018-02-07 15:04:27.740

优先级 10:性能

  • 具有奇数核的 CPU

    • 节点 0 分配了 5 个内核。这是一个非常糟糕的 NUMA 配置。

    • 节点 1 分配了 5 个内核。这是一个非常糟糕的 NUMA 配置。

优先级 20:文件配置

  • C 驱动器上的 TempDB tempdb - tempdb 数据库在 C 驱动器上有文件。TempDB 经常不可预测地增长,使您的服务器面临 C 驱动器空间耗尽和严重崩溃的风险。C 通常也比其他驱动器慢得多,因此性能可能会受到影响。

优先级 50:可靠性

  • 最近在默认跟踪中记录的错误
    • master - 2018-03-07 08:43:11.72 登录错误:17892,严重性:20,状态:1。2018 年 3 月 7 日 08:43:11.72 登录 由于触发器执行,登录“example_user”登录失败。[客户:IPADDR]

(注意:由于启用了限制用户会话的触发器而导致的许多错误 - 用于 ERP 许可使用控制)

  • 页面验证不是最佳的

    • DATABASE_A - 数据库 [DATABASE_A] 没有用于页面验证的 NONE。SQL Server 可能更难识别存储损坏并从中恢复。考虑使用 CHECKSUM 代替。

    • DATABASE_B - 数据库 [DATABASE_B] 没有用于页面验证的 NONE。SQL Server 可能更难识别存储损坏并从中恢复。考虑使用 CHECKSUM 代替。

    • DATABASE_C - 数据库 [DATABASE_C] 没有用于页面验证的 NONE。SQL Server 可能更难识别存储损坏并从中恢复。考虑使用 CHECKSUM 代替。

    • DATABASE_D - 数据库 [DATABASE_D] 没有用于页面验证的 NONE。SQL Server 可能更难识别存储损坏并从中恢复。考虑使用 CHECKSUM 代替。

    • DATABASE_E - 数据库 [DATABASE_E] 没有用于页面验证的 NONE。SQL Server 可能更难识别存储损坏并从中恢复。考虑使用 CHECKSUM 代替。

    • DATABASE_F - 数据库 [DATABASE_F] 没有用于页面验证的 NONE。SQL Server 可能更难识别存储损坏并从中恢复。考虑使用 CHECKSUM 代替。

    • DATABASE_G - 数据库 [DATABASE_G] 没有用于页面验证的 NONE。SQL Server 可能更难识别存储损坏并从中恢复。考虑使用 CHECKSUM 代替。

    • DATABASE_H - 数据库 [DATABASE_H] 没有用于页面验证的 NONE。SQL Server 可能更难识别存储损坏并从中恢复。考虑使用 CHECKSUM 代替。

    • DATABASE_I - 数据库 [DATABASE_I] 没有用于页面验证的 NONE。SQL Server 可能更难识别存储损坏并从中恢复。考虑使用 CHECKSUM 代替。

    • DATABASE_Z - 数据库 [DATABASE_Z] 没有用于页面验证的 NONE。SQL Server 可能更难识别存储损坏并从中恢复。考虑使用 CHECKSUM 代替。

    • DATABASE_K - 数据库 [DATABASE_K] 没有用于页面验证的 NONE。SQL Server 可能更难识别存储损坏并从中恢复。考虑使用 CHECKSUM 代替。

    • DATABASE_J - 数据库 [DATABASE_J] 没有用于页面验证的 NONE。SQL Server 可能更难识别存储损坏并从中恢复。考虑使用 CHECKSUM 代替。

    • DATABASE_L - 数据库 [DATABASE_L] 没有用于页面验证的 NONE。SQL Server 可能更难识别存储损坏并从中恢复。考虑使用 CHECKSUM 代替。

    • DATABASE_M - 数据库 [DATABASE_M] 没有用于页面验证的 NONE。SQL Server 可能更难识别存储损坏并从中恢复。考虑使用 CHECKSUM 代替。

    • DATABASE_O - 数据库 [DATABASE_O] 没有用于页面验证的 NONE。SQL Server 可能更难识别存储损坏并从中恢复。考虑使用 CHECKSUM 代替。

    • DATABASE_P - 数据库 [DATABASE_P] 没有用于页面验证的 NONE。SQL Server 可能更难识别存储损坏并从中恢复。考虑使用 CHECKSUM 代替。

    • DATABASE_Q - 数据库 [DATABASE_Q] 没有用于页面验证的 NONE。SQL Server 可能更难识别存储损坏并从中恢复。考虑使用 CHECKSUM 代替。

    • DATABASE_R - 数据库 [DATABASE_R] 没有用于页面验证的 NONE。SQL Server 可能更难识别存储损坏并从中恢复。考虑使用 CHECKSUM 代替。

    • DATABASE_S - 数据库 [DATABASE_S] 没有用于页面验证的 NONE。SQL Server 可能更难识别存储损坏并从中恢复。考虑使用 CHECKSUM 代替。

    • DATABASE_T - 数据库 [DATABASE_T] 没有用于页面验证的 NONE。SQL Server 可能更难识别存储损坏并从中恢复。考虑使用 CHECKSUM 代替。

    • DATABASE_U - 数据库 [DATABASE_U] 没有用于页面验证的 NONE。SQL Server 可能更难识别存储损坏并从中恢复。考虑使用 CHECKSUM 代替。

    • DATABASE_V - 数据库 [DATABASE_V] 没有用于页面验证的 NONE。SQL Server 可能更难识别存储损坏并从中恢复。考虑使用 CHECKSUM 代替。

    • DATABASE_X - 数据库 [DATABASE_X] 没有用于页面验证的 NONE。SQL Server 可能更难识别存储损坏并从中恢复。考虑使用 CHECKSUM 代替。

  • 远程 DAC 已禁用 - 未启用对专用管理连接 (DAC) 的远程访问。当 SQL Server 没有响应时,DAC 可以使远程故障排除变得更加容易。

优先级 50:服务器信息

  • 未启用即时文件初始化 - 考虑启用 IFI 以加快恢复和数据文件增长。

优先级 100:性能

  • 填充因子已更改

    • DATABASE_A - [DATABASE_A] 数据库有 417 个对象,填充因子 = 70%。这可能会导致内存和存储性能问题,但也可能会阻止页面拆分。

    • DATABASE_B - [DATABASE_B] 数据库有 318 个对象,填充因子 = 70%。这可能会导致内存和存储性能问题,但也可能会阻止页面拆分。

    • DATABASE_C - [DATABASE_C] 数据库有 346 个对象,填充因子 = 70%。这可能会导致内存和存储性能问题,但也可能会阻止页面拆分。

    • DATABASE_D - [DATABASE_D] 数据库有 663 个对象,填充因子 = 70%。这可能会导致内存和存储性能问题,但也可能会阻止页面拆分。

    • DATABASE_E - [DATABASE_E] 数据库有 335 个对象,填充因子 = 70%。这可能会导致内存和存储性能问题,但也可能会阻止页面拆分。

    • DATABASE_F - [DATABASE_F] 数据库有 1705 个对象,填充因子 = 70%。这可能会导致内存和存储性能问题,但也可能会阻止页面拆分。

    • DATABASE_G - [DATABASE_G] 数据库有 671 个对象,填充因子 = 70%。这可能会导致内存和存储性能问题,但也可能会阻止页面拆分。

    • DATABASE_H - [DATABASE_H] 数据库有 2364 个对象,填充因子 = 70%。这可能会导致内存和存储性能问题,但也可能会阻止页面拆分。

    • DATABASE_I - [DATABASE_I] 数据库有 1658 个对象,填充因子 = 70%。这可能会导致内存和存储性能问题,但也可能会阻止页面拆分。

    • DATABASE_Z - [DATABASE_Z] 数据库有 673 个对象,填充因子 = 70%。这可能会导致内存和存储性能问题,但也可能会阻止页面拆分。

    • DATABASE_K - [DATABASE_K] 数据库有 312 个对象,填充因子 = 70%。这可能会导致内存和存储性能问题,但也可能会阻止页面拆分。

    • DATABASE_J - [DATABASE_J] 数据库有 864 个对象,填充因子 = 70%。这可能会导致内存和存储性能问题,但也可能会阻止页面拆分。

    • DATABASE_L - [DATABASE_L] 数据库有 1170 个对象,填充因子 = 70%。这可能会导致内存和存储性能问题,但也可能会阻止页面拆分。

    • DATABASE_M - [DATABASE_M] 数据库有 382 个对象,填充因子 = 70%。这可能会导致内存和存储性能问题,但也可能会阻止页面拆分。

    • DATABASE_O - [DATABASE_O] 数据库有 356 个对象,填充因子 = 70%。这可能会导致内存和存储性能问题,但也可能会阻止页面拆分。

    • msdb - [msdb] 数据库有 8 个对象,填充因子 = 70%。这可能会导致内存和存储性能问题,但也可能会阻止页面拆分。

    • DATABASE_P - [DATABASE_P] 数据库有 291 个对象,填充因子 = 70%。这可能会导致内存和存储性能问题,但也可能会阻止页面拆分。

    • DATABASE_Q - [DATABASE_Q] 数据库有 343 个对象,填充因子 = 70%。这可能会导致内存和存储性能问题,但也可能会阻止页面拆分。

    • DATABASE_R - [DATABASE_R] 数据库有 2048 个对象,填充因子 = 70%。这可能会导致内存和存储性能问题,但也可能会阻止页面拆分。

    • DATABASE_S - [DATABASE_S] 数据库有 325 个对象,填充因子 = 70%。这可能会导致内存和存储性能问题,但也可能会阻止页面拆分。

    • DATABASE_T - [DATABASE_T] 数据库有 322 个对象,填充因子 = 70%。这可能会导致内存和存储性能问题,但也可能会阻止页面拆分。

    • DATABASE_U - [DATABASE_U] 数据库有 351 个对象,填充因子 = 70%。这可能会导致内存和存储性能问题,但也可能会阻止页面拆分。

    • DATABASE_V - [DATABASE_V] 数据库有 312 个对象,填充因子 = 70%。这可能会导致内存和存储性能问题,但也可能会阻止页面拆分。

    • DATABASE_X - [DATABASE_X] 数据库有 352 个对象,填充因子 = 70%。这可能会导致内存和存储性能问题,但也可能会阻止页面拆分。

    • tempdb - [tempdb] 数据库有 2 个对象,填充因子 = 70%。这可能会导致内存和存储性能问题,但也可能会阻止页面拆分。

  • 一个查询的多个计划 - 计划缓存中的单个查询有 20763 个计划 - 这意味着我们可能存在参数化问题。

  • 服务器触发器已启用 - 服务器触发器 [connection_limit_trigger] 已启用。确保您了解触发器正在做什么 - 它所做的工作越少越好。

  • 带重新编译的存储过程

    • master - [master].[dbo].[sp_AllNightLog] 在存储过程代码中有 WITH RECOMPILE,这可能会由于代码的不断重新编译而导致 CPU 使用率增加。

    • master - [master].[dbo].[sp_AllNightLog_Setup] 在存储过程代码中有 WITH RECOMPILE,这可能会由于代码的不断重新编译而导致 CPU 使用率增加。

优先级 110:性能

  • 没有聚集索引的活动表

    • DATABASE_A - [DATABASE_A] 数据库有堆 - 没有聚集索引的表 - 正在被主动查询。

    • DATABASE_B - [DATABASE_B] 数据库有堆 - 没有聚集索引的表 - 正在被主动查询。

    • DATABASE_C - [DATABASE_C] 数据库有堆 - 没有聚集索引的表 - 正在被主动查询。

    • DATABASE_E - [DATABASE_E] 数据库有堆 - 没有聚集索引的表 - 正在被主动查询。

    • DATABASE_F - [DATABASE_F] 数据库有堆 - 没有聚集索引的表 - 正在被主动查询。

    • DATABASE_H - [DATABASE_H] 数据库有堆 - 没有聚集索引的表 - 正在被主动查询。

    • DATABASE_I - [DATABASE_I] 数据库有堆 - 没有聚集索引的表 - 正在被主动查询。

    • DATABASE_K - [DATABASE_K] 数据库有堆 - 没有聚集索引的表 - 正在被主动查询。

    • DATABASE_O - [DATABASE_O] 数据库有堆 - 没有聚集索引的表 - 正在被主动查询。

    • DATABASE_Q - [DATABASE_Q] 数据库有堆 - 没有聚集索引的表 - 正在被主动查询。

    • DATABASE_S - [DATABASE_S] 数据库有堆 - 没有聚集索引的表 - 正在被主动查询。

    • DATABASE_T - [DATABASE_T] 数据库有堆 - 没有聚集索引的表 - 正在被主动查询。

    • DATABASE_U - [DATABASE_U] 数据库有堆 - 没有聚集索引的表 - 正在被主动查询。

    • DATABASE_V - [DATABASE_V] 数据库有堆 - 没有聚集索引的表 - 正在被主动查询。

    • DATABASE_X - [DATABASE_X] 数据库有堆 - 没有聚集索引的表 - 正在被主动查询。

优先级 150:性能

(注:这里有很多建议,但由于字数限制,我无法包括它们。如果有其他方式分享,请指出。)

Tho*_*ter 7

你给了我们一个很长(而且非常详细)的问题。现在你必须处理一个很长的答案。;)

我建议在您的服务器上更改几件事。但让我们从最紧迫的问题开始。

一次性应急措施:

在您的系统上部署索引后性能令人满意且性能缓慢下降的事实非常强烈地暗示您需要开始维护统计数据并(在较小程度上)处理索引框架。

作为一项紧急措施,我建议对所有数据库进行一次性手动统计更新。您可以通过执行此脚本来获取必要的 TSQL:

DECLARE @SQL VARCHAR(1000)  
DECLARE @DB sysname  

DECLARE curDB CURSOR FORWARD_ONLY STATIC FOR  
   SELECT [name]  
   FROM master..sysdatabases 
   WHERE [name] NOT IN ('model', 'tempdb') 
   ORDER BY [name] 

OPEN curDB  
FETCH NEXT FROM curDB INTO @DB  
WHILE @@FETCH_STATUS = 0  
   BEGIN  
       SELECT @SQL = 'USE [' + @DB +']' + CHAR(13) + 'EXEC sp_updatestats' + CHAR(13)  
       PRINT @SQL  
       FETCH NEXT FROM curDB INTO @DB  
   END  

CLOSE curDB  
DEALLOCATE curDB
Run Code Online (Sandbox Code Playgroud)

它由 Tim Ford 在mssqltips.com 上的博客文章中提供,他还解释了更新统计信息的重要性。

请注意,这是一项 CPU 和 IO 密集型任务,不应在业务时间内完成。

如果这解决了您的问题,请不要停在那里!

定期维护:

看看 Ola Hallengren维护解决方案,然后至少设置以下两项工作:

  • 统计更新作业(如果可能的话,每晚)。您可以在代理作业中使用此 CMD 代码。这项工作必须从头开始创建。

sqlcmd -E -S $(ESCAPE_SQUOTE(SRVR)) -d MSSYS -Q "EXECUTE dbo.IndexOptimize @Databases = 'USER_DATABASES', @FragmentationLow = NULL, @FragmentationMedium = NULL, @FragmentationHigh = NULL, @UpdateStatistics = 'ALL', @OnlyModifiedStatistics = 'Y', @MaxDOP = 0, @LogToTable = 'Y'" -b

  • 索引维护工作。我建议从每月一次的预定执行开始。您可以从 Ola 为 IndexOptimize 作业提供的默认值开始。

我建议第一份工作单独更新统计数据有几个原因:

  • 索引重建只会更新该索引覆盖的列的统计信息,而索引重组根本不会更新统计信息。Ola 将碎片分为三类。默认情况下,只会重建类别高索引。
  • 索引未涵盖的列的统计信息将仅由 IndexOptimize 作业更新。
  • 缓解升序键问题

如果默认设置为启用状态,SQL Server 将自动更新统计信息。问题是阈值(SQL Server 2016 的问题不大)。当一定数量的行发生变化时(在旧版本的 SQL Server 中为 20%),统计信息会更新。如果您有大表,在更新统计信息之前这可能会发生很多变化。在此处查看有关阈值的更多信息。

既然你正在做 CHECKDB,我可以告诉你可以像以前一样继续做,或者你也使用维护解决方案。

有关索引碎片和维护的更多信息,请查看:

SQL Server 索引碎片概述

不再担心 SQL Server 碎片化

考虑到您的存储子系统,我建议不要过多关注“外部碎片”,因为无论如何数据都不会按顺序存储在您的 SAN 上。

优化您的设置

sp_Blitz 脚本为您提供了一个很好的开始列表。

优先级 20:文件配置 - C 驱动器上的 TempDB: 与您的存储管理员交谈。询问他们您的 C 驱动器是否是 SQL Server 可用的最快磁盘。如果没有,请将您的 tempdb 放在那里...期间。然后检查您有多少 temdb 文件。如果答案是一种解决方法。如果它们的大小不同,请修复这两个。

优先级 50:服务器信息 - 未启用即时文件初始化:按照 sp_Blitz 脚本提供的链接启用 IFI。

优先级 50:可靠性 - 页面验证不是最佳的:您应该将其设置回默认值 (CHECKSUM)。按照 sp_Blitz 脚本提供的链接并按照说明操作。

Priority 100:Performance - Fill Factor Changed: 问问自己为什么有这么多对象的填充因子设置为 70。如果您没有答案并且没有应用程序供应商严格要求它。将其设置回 100%。

这基本上意味着 SQL Server 将在这些页面上留下 30% 的空白空间。因此,要获得相同数量的数据(与 100% 的完整页面相比),您的服务器必须多读取 30% 的页面,并且它们将多占用 30% 的内存空间。经常这样做的原因是为了防止索引碎片。

但同样,您的存储无论如何都将这些页面保存在不同的块中。所以我会把它设置回 100% 并从那里拿走它。

如果每个人都快乐,该怎么办:

  • 查看 sp_Blitz 的其余输出并决定是否按照建议更改它们。
  • 执行 sp_BlitzIndex 并查看您创建的索引,如果它们被使用或可能有机会添加/更改索引。
  • 查看您的 Query Store 数据(按照 Peter 的建议)。您可以在此处找到介绍。
  • 享受 DBA 应得的摇滚明星现场。;)


Nel*_*pes 3

不要忽视您所有非常有用且我已应用或将要应用的答案,最大的问题并不容易找到。

在我们发出最后一条消息后的几天里,问题变得更加严重。

由于我们基于云,我和管理基础设施并为我们提供支持的公司都无法访问物理主机。

当我注意到有时处理器的平均利用率为 20%,而另一些日子则高得多,超过 60%,而此时工作负载虽然不完全相同,但相似时,我感到有些奇怪。有相同数量的人执行或多或少相同类型的操作。

本周早些时候,用户开始卡住几分钟,只有处理器被勒死。我要求几个用户注销(那些花费了更多资源但仍然没有任何异常的用户),我关闭了与数据库链接的各种服务,最终没有任何改变。我要求支持我们并且可以与我们的云人员进行通信的系统管理员远程到我的机器以查看我所看到的内容并帮助我找到一些东西,因为我无法更好地找到问题。

技术人员也没有发现任何东西。当他联系云时,他终于开始给我一些理由,说明一定有其他原因导致了这个问题。在云中,他们没有意识到任何事情,只是因为物理主机之间配置了负载平衡,所以支持我们 SQL Server 的虚拟机当天在物理主机之间移动了几次。幸运的是,我准确地告诉了我们的技术人员当天问题开始发生的时间,这与虚拟机上次移动到当天剩余时间没有离开的物理主机的时间一致。

如果技术人员没有密切关注这个问题,这将是他甚至可以与云人员交谈的时候,但当他们看到性能样本时,他们不会得到任何东西,因为云再次只看到了CPU 大约为 40/50% 的样本,而实际上平均高于 80%,并且经常停留在 100%。

现在机器站在物理主机上(不在主机之间移动),虽然我们还没有达到完美的性能,但每个人都在工作并给出了更多积极的反馈,因为我们所有用户的平均 CPU 约为 20%,服务。

同时,我们还将 tempdb 放在另一个磁盘上(它位于操作系统磁盘上),并增加了文件,以更符合 CPU 的核心数量。

核心数量也根据sp_Blitz的建议进行了调整。

还有一个自动例程,根据旧日期运行一整天......并且由于我们到达的早上它还没有结束,而且我们无法检查它是否正在运行,所以我仍然开始手动运行。但另一个可能仍在运行,并且在那段时间运行了两次。我们更改了日期以减少所需时间,现在已经是深夜了。但这不是解决方案,因为它在我们遇到的许多问题(如这里描述的问题)之前就已经解决了。

我们还设法让 ERP 助理安排了与制造商的会议,因此我们将展示我们的系统并寻求建议,并澄清一些疑虑,因为培训视频中的建议与大多数内容相反包括 Microsoft 本身的建议,例如优先级提升和填充因子 70%。

由于该应用程序还有一个维护屏幕,因此我将查找这些维护所需的周期,以及在应用程序之外还需要做什么。我的想法是使用 Ola Hallengren 的计划。

我相信 Thomas Kronawitter 的答案是绝对正确的,并且我正在应用它,但是,我认为这个描述对其其他人来说可能很重要,在遵循所有良好实践之后仍然无法解决问题,因为它可能在物理主机中。谢谢托马斯。