导出 (0) 打印
全部展开

Windows Azure SQL Database 和 SQL Server -- 性能和可扩展性比较

更新时间: 2014年1月

作者Conor CunninghamTobias TernströmSilvano CorianiEwan Fairweather

供稿作者Ralph SquillaceKarthika Raman

尽管 SQL Server 和 Windows Azure SQL Database(简称为 SQL Database,以前称为 SQL Azure)具有许多重要的相似之处,但这两者并不完全相同;而且,虽然这些差异相对较小,但应用程序在 SQL Database 上的执行方式与在 SQL Server 上的执行方式会有所不同。因此,针对每个平台的应用程序体系结构和性能评估技术也会有差异。

本文对这些性能差异及其原因进行了说明,并且包含了现实客户在实际生产环境中使用 SQL Database 时解决有关性能问题的经验之谈。本文还介绍了不适用于 SQL Database 的一些常见的 SQL Server 性能评估技术。有关高性能和大规模 Windows Azure 应用程序设计的更广泛论述,请参阅在 Windows Azure 云服务上设计大规模服务的最佳实践

请注意,SQL Server 除了本地运行以外,还可以在 Windows Azure 虚拟机中运行。本文档中所述的比较和方法适用于 SQL Server,无论它是在本地环境中还是在 Windows Azure 虚拟机中运行。但是,当 SQL Server 在 Windows Azure 虚拟机中运行时,需要额外考虑一些性能因素。在 Windows Azure 虚拟机中 SQL Server 的性能指南一文中详细介绍了这些因素。

Windows Azure SQL Database 可通过高级版提供预留数据库资源的功能。有关详细信息,请参阅 Windows Azure SQL Database 高级版指南

本文的结构如下。

在第一部分中,介绍了几个标准应用程序方案以及这些方案通常采用的应用程序模式和方法。

在第二部分中,介绍了客户对 SQL Database 的几个共同期望,并且论述了这些期望可能导致应用程序方法鲜有成功的几种方式。

在第三部分的主题中,首先强调了在共享环境中实现良好性能的典型模式,然后,特别针对 SQL Database 稍微深入地探讨了一些性能模式。

在第四部分的主题中,概述了针对关系数据库的几个常规性能评估技术。

在第五部分的主题中,论述了用于正确评估 SQL Database 实例性能的若干技术(此处假定您所使用的功能与 SQL Server 的那些功能不同)。

第六部分是附录,提供了若干 SQL 脚本,这些脚本可帮助您对 SQL Database 实例进行评估和故障排除。

在执行性能分析和优化时,由于不同的体系结构和技术要求,不同的应用程序模式要求采用不同的方法。由于这些差异的存在,想要定义一组用于测量和了解性能特性的通用最佳实践和技术就会变得很困难。尽管如此,仍有一些通用模式可用来对应用程序进行分类,并且归纳指导方针。本部分介绍迄今在 Windows Azure SQL Database 应用程序中可以看到的一些通用模式。

此类应用程序包含转向软件即服务(或 SaaS)交付模型的传统应用程序(会计、CRM、ERP 等),并且通常与实现新的业务模型或扩展新的客户群体有关。在向新的软件交付模型迁移时,传统软件开发公司遇到的最大挑战就是如何解决与它们现有的代码库以及员工技能和方法有关的问题。迁移到 SaaS 模型本身就意味着采用多租户模型作为在同一组物理资源上承载多个客户的方法。这样做可以最大限度发挥硬件投资的效用,并且降低管理成本,但通常要求重新设计应用程序,对数据层级别予以特别关注,从而在保持数据一致性的同时将数据分散到若干数据库上。大家有一个共同的期望,就是该平台在执行上能“和本地一样”,尽管该平台是托管环境并且硬件资源通常与其他应用程序共享。

这个方案通常涉及从成本高昂的环境中迁移现有的批处理应用程序,以便在许可和维护方面都能够节约成本。对于大型机应用程序,许多现有的业务逻辑(通常为 COBOL)都必须保留且不进行任何修改,因为保留重新编写代码所需的内部知识既复杂又困难。如果应用程序是很久之前编写的,则更是如此。必须仔细对这些环境进行评估,以便了解需要进行多大程度的优化以便它们可以在 Azure 上运行良好。尽管这些工作可能乏善可陈,但经验表明,这些类型的应用程序往往没有严格的延迟要求,而是只要求足够的吞吐量以便在要求的时间段完成大批作业。

Windows Azure 之类的云平台通常是适合于自定义应用程序项目的理想环境。这些应用程序通常是在各个项目的基础上开发的,并且利用在多个项目的基础上开发、优化和重复使用的框架来实现新的业务流程。针对此类型软件开发工作的一个共同要求是云平台能够支持混合和连接功能,例如数据同步和复制、进程业务流程、集成等。上市时间以及解决方案快速开发和部署对于这些类型的项目而言至关重要。它们还需要一种能够立即发现、分析和解决性能瓶颈和可伸缩性问题的方法。

此模式代表着已在云环境中直接创建的重要的新软件开发项目,这些项目通常涉及某种程度的业务风险。在云中开发、测试这些类型的应用程序并将它们投入生产 – 在其他任何地方都无法这样做。因为在大规模运行应用程序和服务中存在固有的复杂性,所以,理解应用程序以及承载它的云平台的行为和性能非常重要,这可以让您尽可能全面地理解它是如何适合业务要求、成本模型以及性能和规模的期望。运行此类型的应用程序通常要求在开发成本和投资风险之间进行某种程度的权衡。

此类型的应用程序通常意味着全新的可能性:在没有云平台提供的弹性和可大规模缩放环境的情况下,此类型的应用程序将可能永远不会存在。应用程序提供商以前往往试图在传统的数据中心中使用传统的体系结构来实现此类应用程序,但由于用户群的工作负荷无法预测,而且在传统基础结构之上设计能够应对峰值负荷的解决方案十分困难且成本高昂,因此在实施阶段就失败了,更不必说在设计和生成后对应用程序进行管理了。为了满足常见的高可见性和业务关键要求,我们需要采用一种全新的方法,才能设计和实施在应用程序层和数据层上都完全可扩展和分区的解决方案。如果需要更高程度的复杂性,那么,从实施的初期阶段开始,我们就必须能够快速分析端到端应用程序的性能,并能够对性能问题进行故障排除。

新的 SQL Database 用户都持有一个共同的想法,那就是:

SQL Database 是以服务形式提供的 SQL Server 版本,具有成本低、灵活性高、高度可用且管理开销低廉的特点。

这个想法基本上是正确的。SQL Database 与 SQL Server 非常相似:

  • 它支持很大一部分的 SQL Server 可编程性外围应用(包括支持 TDS 和 T-SQL、具有 master 数据库和使用 SQL 身份验证等)。

  • 它重复使用 SQL Server 引擎版本来用于其核心 RDBMS 功能。

  • 客户只需重新学习很少的知识,即可开始使用这个新平台。

但是,生产应用程序却不是在这个抽象级别上构建的。因此,在设计应用程序时,全面领悟并且深刻理解 SQL Server 和 SQL Database 之间的主要差异、这些差异产生的原因以及在 SQL Database 中要做哪些不同的工作才能确保您的应用程序高效顺畅地运行,就显得十分重要。

在下面列举的三个重要示例中,从 SQL Server 中获得的经验并不适用于 SQL Database。下面三个方面是在您设计应用程序时要掌握的三个最重要的方面,只有理解了这三个方面,才能设计出规模和性能都很适当且成本在现在和将来都很适宜的应用程序。

  • 延迟

  • 硬件

  • 资源容量

下面将详细介绍每一项。

 

概念 SQL Server SQL Database

延迟

多台服务器放置在一起并且可精确控制这些服务器的连接方式。应用程序服务器和 SQL Server 之间的延迟通常在亚毫秒级别。在大多数情况下,为完成单个操作而执行大量数据库调用的应用程序在执行速度上是可接受的。

在 SQL Database 中,Azure 服务和 SQL Database 之间的通信通常在毫秒级别,比本地高一个数量级。

硬件

公司需要购买企业级别的硬件并且配备 IT 员工,以便确保为工作准备了适当的计算机。计算机出现故障的情况非常少见,并且很少需要花时间来构建命令或连接弹性,例如在应用程序中配置操作失败时的自动重试逻辑。升级和维护按公开的时间表来实施计划停机。

Windows Azure 是为云经济构建的,并且在整个服务中使用商用硬件和类似计算机。与通常在本地使用的那些计算机相比,这些计算机的性能没有那么强大且容错能力也没有那么高,并且可能随时对特定计算机进行维护。尽管数据有冗余保护措施,但可能会发生与连接相关的错误。

资源容量

应用程序的数据库通常在单独的计算机上承载,而不与其他应用程序数据库共享硬件。

单台计算机可以承载多达数百个数据库,并且每个数据库都可能来自不同应用程序并且共享计算机资源。服务中的逻辑将数据库移到不同的宿主计算机,以便分散服务的总体负载并且提供资源的公平使用。

通过引入高级数据库 (Premium Database),您现在可以为 Windows Azure SQL Database 预留一定的容量。通过为 SQL Database 及其辅助副本预留固定的容量,相对于现有 SQL Database Web 和企业版,高级版可为云应用程序提供更可预测的性能。

高级数据库与 SQL Database 的其他版本相比,还大大减少了多租户问题。

对于上面的示例,将应用程序迁移到 SQL Database 或构建 SQL Database 应用程序的最常见原因是什么?

  • 弹性。我们想要更快地获得容量并且不在硬件方面进行投资。

  • 低管理开销。大多数可管理性内置于 SQL Database 中。最重要的是,只需要执行 CREATE DATABASE 命令,就可以立即提供一个高度可用的数据库,并且包含 3-4 个位于不同机架的不同物理计算机上的副本。

  • 价格。我们想要以较低的成本运行我们的服务。

尽管上面的信息只是概括性地介绍了一些概念,并没有涉及核心 RDBMS 功能中的基本差异,但基础数据库引擎的执行就是围绕这些概念进行的,因此,在设计您的应用程序时考虑这些概念十分重要。

理解如何选择正确的应用程序设计十分重要,这样才会尽可能减少 SQL Database 的使用问题并且获得最大利益。

对于典型的 SQL Server 应用程序,与某一应用程序相关联的所有内容可能都放置于一个数据库中(数据库可能很大);但是对于典型的 Azure SQL Database 应用程序,将应用程序的主要功能拆分到若干数据库中,取得的效果才会最好。这些单独的数据库利用多台计算机(而不是一台计算机)的资源,并且此方法简化了随着数据库增长将单个应用程序的作业功能拆分为多个数据库的工作。

其次,在 SQL Database 中使用商用硬件和维护模型意味着您应该生成相应的命令,以便为某些操作将失败并且需要重试的情况作好准备。这就需要考虑如何将操作拆分为若干更小的部分、避免长时间处于临时状态以及使操作更易于重新启动。借助于这一编程模型,即使在程序执行过程中数据库服务器发生故障转移,应用程序也会继续工作。

基于 SQL Database 的应用程序还使用不同的方法进行故障排除和日志记录。与 Web 服务器相似,这些应用程序的故障排除模型比主动的故障排除(直接查询 DMV 等)更适合于日志记录。然后,这些日志流将成为应用程序管理的一部分,并且提供仪表板,显示应用程序的哪些部分正常运行、哪些部分需要注意。在实现时,此模型通过引入更多的自动化环节,使用很少的人力即可实现更大规模的应用程序。

延迟是在 SQL Database 应用程序中要考虑的另一个重要方面。因为应用程序的某些部分对于您的用户来说不是本地托管的,所以,这常常要求采用异步执行/查询模式,以便应用程序的响应性更好。在其他情况下,它要求应用程序在完成一个操作时考虑如何更为慎重地选择所需的往返次数(与等效的本地 SQL Server 应用程序相比)。在开发适合于 SQL Database 的应用程序时,这些差异表现为不同的模式和关注领域。

最后,最好在将要运行托管应用程序的相同生产硬件上测试该应用程序的性能,但是,在共享的托管硬件上进行的性能测试在细节方面不同于在一组专用计算机上进行的性能测试。在本文的第 5 部分中将更详细地介绍这些差异。

在设计应用程序时,有四个可用于承载应用程序的 SQL 部分的基本选项:

  1. 实际的 SQL Server(即,不是虚拟的)

  2. 本地/托管 VM 中的 SQL Server

  3. Windows Azure 虚拟机中的 SQL Server

  4. Windows Azure SQL Database

很明显,从方案 1 到方案 4,所节约的 OPEX 和 COGS 成本呈上升趋势,但使用向上扩展技术(例如专用硬件)以及与性能相关的特定 SQL Server 功能解决性能问题的能力则呈降低趋势。如果在虚拟环境(例如 SQL Database)中发现了性能问题,则需要通过更改应用程序(而不是提升硬件)来解决这些问题。例如,可以将应用程序更改为实现某种类型的向外扩展模式,或将应用程序的不同任务划分到存储桶中,其中某些任务必须给予较多的资源并且允许同步运行(例如银行事务),而其他任务可以排入队列并且使用较少的资源来执行(例如,向银行客户发送帐户汇总电子邮件)。

在前十年中,我们关注的是通过引入并全面采用对象关系映射程序之类的技术(实体框架、Hibernate、NHibernate 等),使开发人员能够更容易地创建应用程序。尽管此类技术可缩短解决方案的开发时间,但它们也导致了数据库要执行的核心任务以及它们对性能和规模的影响与开发人员脱节。在典型数据中心的使用率百分比常常是个位数的时代(云之前),以这些技术为基础、执行完全未优化的数据库调用的那些应用程序,即便是使用率非常高的应用程序,为了能够有效运行,唯一的办法就是过度提供大量硬件资源。

在过去的十年中,运行应用程序的实际成本在很大程度上不为开发人员所知;普通的开发人员不需要太关心应用程序的实际运行成本。在您询问较大的客户对其应用程序的运行成本是否有着深入了解时,将会出现下面这种典型模式。这些客户通常知道他们在大型系统(例如主要的 ERP)上的花费是多少,但他们运行并维护的几百个其他应用程序都会被归入“其他应用程序”范畴。他们只是用所有应用程序的总成本减去主要应用程序(例如 ERP 系统)的成本,来得出这些其他应用程序的总成本;与单个应用程序的成本有关的细节很少存在。

因为许多客户不知道单个应用程序的成本,所以,对于维护该应用程序的开发团队来说,他们很少知道这个未指定的成本。在此环境中,开发人员没有什么动力来降低该应用程序的操作成本。Windows Azure 颠覆了这个观念,因为来自云提供商的发票可以分解为运行特定应用程序的各个 Azure VM、Web 和辅助角色以及 SQL Database 的成本。

这不仅仅是帐单问题;通过研究各个成本,企业可了解相应的信息,并在解决应用程序性能问题时使用这些信息作出正确决策。例如,可以通过以下方式解决性能问题:

  • 增加 Azure 中应用程序辅助角色和数据库的资源

  • 使用高级版选项来预留 CPU、磁盘 IO 和内存等资源。

  • 重新设计应用程序的结构以便减少计费资源的使用量

  • 将其移到本地并购买定制硬件

结论

让开发人员调整到应用程序总拥有成本 = 开发成本 + 服务成本的云心态十分重要。有时候,将投资应用于应用程序开发方面以降低服务成本可能是正确的选择;而在其他时候,将投资应用于增加应用程序资源可能更加明智。这个选择还可能受到组织的技能集以及他们需要解决的问题规模的影响。如果在应用程序开发方面具有坚实的背景,则在决策时应该倾向于对应用程序进行更改以便减少应用程序的资源需求;而在其他情况下,更好的选择可能是在使用该平台提供的更多资源方面进行投资,以便实现所需性能目标。

为了高效地运行应用程序,通过遥测了解应用程序对任何云服务的使用十分重要。在 Windows Azure 中,我们建议评估 New Relic 或 Opstera 等第三方工具,这些工具很不错;但基于 Windows Azure 诊断的自定义解决方案也是值得考虑的选项之一。最后,要尽量避免复杂性过高,例如在上面的 ORM 例子中,复杂性过高可能将您引入歧途:性能或可伸缩性问题很难解决或者解决起来成本太高。

本部分将说明影响数据库操作性能的常见因素。第一小节介绍在 SQL Server 中使用的传统因素。请注意,其中大多数因素对 SQL Database 也适用。第二部分介绍 SQL Database 中新产生的原因。最后,我们将演练 Azure SQL Database 提供的故障排除和诊断工具的外围应用,并且使用一个实际示例来展示如何使用这些工具。

SQL Server 中的性能分析说来话长 – 本部分应该可以帮助您对此有一个基本了解。有关详细信息,请参阅:http://msdn.microsoft.com/en-us/library/dd672789(v=SQL.100).aspx

SQL Server 的查询优化器执行影响系统性能的计划选择。例如,查询优化器可以决定执行索引查找或表扫描,以便满足某一给定的查询。该查询优化器的选择通常是正确的,但它所做的选择也有不合适的情况,这可能是因为输入数据(例如统计信息)过期,或者是因为查询在优化器中达到了模型限制(例如,在评估基数时没有推断出在所有情况下跨联接出现数据扭曲的原因)。

优化器可能会单独(影响单个查询)或一起(其中,系统的整体性能由于一组计划选择而不太理想)作出不佳的计划选择。优化器(而不是基本的计划中搜索空间试探方法)并不考虑跨计划优化。

有关优化器的详细信息,请参考《深入解析 SQL Server 2008》(由 Conor Cunningham 等人撰写)或即将发布的《深入解析 SQL Server 2012》中的“优化器”章节。

SQL Server 在用户的硬件上运行,因此,由管理员来负责购买和配置该硬件。如果客户未正确设置和配置硬件,可能会降低整体系统性能。常见的示例包括:

  • 不同组件(例如磁盘控制器)的驱动程序选择/版本配置不当

  • BIOS 设置

  • NUMA 配置/CPU 关联设置

  • 内存设置(最大服务器内存等)

  • 检查点间隔

  • MAXDOP

这种情况通常由针对新硬件的严谨文档记录和验收前的测试来处理,以便验证系统的所有核心功能(使用 SQLIOSim 之类的工具,该工具可从 http://www.microsoft.com/en-us/download/details.aspx?id=20163 下载)。

“锁定”用于允许多个用户以受控方式对相同数据进行一致的更改。尽管锁定是数据库系统中必需的部分,但创建的锁定并不都是有效的。例如,优化器可能会创建含有多个冲突查询的计划,导致整体系统吞吐速度同其他可能的计划选择相比更慢。有时候,优化器可能会创建导致死锁的计划组合(例如,在两个或更多计划中以相反的顺序获取不同的锁)。在这种情况下,往往需要不同的计划选择(或者工作负载调用模式)。

闩锁与锁定相似,用于决定对 SQL Server 内的结构的多用户访问。但是,闩锁处理内部物理页结构。闩锁从某种程度上说与锁定是不相干的,但这两个问题都可能出现,并且核心解决方法通常是相似的:如果存在热闩锁,则典型的应对措施是更改调用模式(通过强制实施不同的计划形式或重新安排调用代码),以便减轻阻止闩锁对整体吞吐量的影响。

即使锁定对于数据库中的多用户操作来说不存在问题,它仍可能会导致性能或吞吐量降低,因为某些操作得不到足够的资源,而其他操作则资源超过了需要。尽管此情况的最初表现形式是操作时间超过平常,但导致问题的内在原因是一个或多个资源面临压力。常见因素包括等待类型 SOS_SCHEDULER_YIELD(指示操作正在等待 CPU 变得空闲)、较长的 I/O 等待(指示 SQL Server 生成 I/O 请求的速度超过 I/O 系统能够处理这些请求的速度)和 RESOURCE_SEMAPHORE(内存)。处理此阻止问题的一个常见方法就是购买更大的计算机或更快的组件。

“检查点”是 DBMS 用来将脏页从内存刷新到磁盘以便尽量缩短数据库服务器崩溃时恢复所用时间的过程。这个过程可能导致在 I/O 操作中出现峰值,从而可能降低查询性能并且影响延迟和吞吐量。应用程序通常需要进行负载测试,并且测试周期应该长于检查点频率,这样可以测量检查点的影响并且包括在容量规划和认证中。

note备注
请注意,SQL Server 2012 包含一个名为“间接检查点”的功能(有关详细信息,请参阅 http://msdn.microsoft.com/en-us/library/ms189573.aspx#IndirectChkpt),该功能可通过将检查点的 I/O 负载更均匀地分摊到更长的时间段中,降低检查点的时间点 I/O 开销。这可以降低 I/O 对性能和容量故障排除的影响。

再一次提醒您注意,现在应该已经很明确了,Windows Azure SQL Database 与 SQL Server 并不完全相同。SQL Database 是在不同数据中心中运行的托管服务。它是多租户的,这意味着多个客户共享相同的物理计算机。SQL Database 包含自动功能,例如自动高可用性 (HA) 和自动备份。它在商用硬件上运行,而不是在大型服务器上运行。SQL Database 以服务形式销售给客户,而不是以许可证的形式。所有这些因素都会影响 SQL Database 的整体业务模型,以及您在性能和缩放方面进行考虑的方式。

此外,SQL Database 目前还不提供在数据仓库查询中常用的许多功能(也就是说 SQL Database 的最初关注点是在 OLTP 系统上)。这些功能包括数据库压缩、并行查询、列存储索引和表分区,以及面向数据仓库工作负载的数据库总大小支持和 I/O 子系统。尽管现在可以使用 SQL Database 生成数据仓库解决方案,但必须了解,这些功能差异将会影响与 SQL Server 的比较结果。

本部分说明上述这些重要因素是如何对 SQL Database 上的性能故障排除产生影响的,并且假定客户的关注点在 OLTP 解决方案上。

SQL Database 与 SQL Server 的最大差异之一是前者公开多租户服务。每台物理机器都可以在一台计算机上存储数百(或更多)的用户数据库。这允许 SQL Database 以一个非常低的价格点供您使用(在美国,开始时每个月仅几美元)。这个差别是很重要的:默认情况下,SQL Database 上的不同用户共享着群集中单个计算机上的资源,并且通过将客户分散到群集内的不同计算机上来尽力提供公平性和负载平衡。

因此,对托管数据库运行性能测试的客户所测试的系统可能还包含来自其他用户的多个其他活动数据库。与一般的 SQL Server 相似,在同一硬件上运行有其他客户会影响性能测试的结果。因此,必须特别小心,以免在单个性能测试的基础上得出与 SQL Database 性能有关的最终结论,因为其他用户可能会导致这些结论失去意义。

不过,您可以使用高级版选项预留数据库和副本的资源。这样,您可以为数据库预留所需的 CPU、磁盘 IO 和内存。您还可以在需要容量时通过升级到高级版来使用此选项,而在不再需要容量时则降级到企业或 Web 版。

使用高级数据库能消除在易受延迟影响的操作中性能变动可导致小型查询耗时超出预期的情况。它还可在无重大更改的情况下迁移某些本地应用程序,因为该体验与那些应用程序中采用的传统隔离式体验更加接近。有关详细信息,请参阅 Windows Azure SQL Database 高级数据库指南

SQL Database 是使用大量计算机构建的。单独来说,这些计算机并不是非常强劲;它们是标准的、基于机架的服务器,在价格和性能上体现了最大的平衡,而不仅仅是考虑整体性能。这个差异很重要,因为许多 SQL Server 应用程序都是基于下面这个假设构建的:它们在“足够快”或“足够大”的计算机上运行(这意味着如果客户曾经在运行应用程序时用尽了资源,就要购买更大的计算机)。SQL Database 在其托管产品中并不会提供非常高端的硬件。SQL Database 是一种“向外扩展”模型,而不是“向上扩展”模型。在某个客户的应用程序超过单台计算机的限制时,这个客户应该重新编写数据库,以便使用多个数据库(分布在多台计算机上),而不是使用单台计算机。

与传统的本地解决方案相比,商用硬件出现故障的几率也更高。商用硬件不提供冗余电源、ECC 内存或在所有情况下都确保足够长运行时间的其他功能。此外,群集是由多台计算机构成的,这就需要这些计算机协同工作,以便在数据层以及应用程序层都提供一个完整的解决方案。目前针对 SQL Database 的 SLA 是:数据库将在 99.9% 的时间可用。换言之,在每个月中,一般的 SQL Database 实例即使有大约 42 分钟不可用,仍能满足该目标。尽管不是每一个数据库都实际满足该目标,但针对大多数数据库的整体可用性数字是满足该目标的。即便如此,这往往会低于传统的 SQL Server 系统(许多 SQL Server 用户不会正式地测量停机时间,但经常会计划停机事件并发送通知 -- 而 SQL Database 故障更具随机性,因为从应用程序角度来说,这些故障通常是计划外的)。这个停机时间意味着,在设计应用程序时,应该通过使用避免单点故障的技术(注重消除总是要依赖单个数据库处于“正常运行”状态的关键途径),将解决方案中每个数据库的停机时间都考虑在内,包括在适当的时候处于其他层中的缓存,以及对连接和命令使用重试逻辑以便更有弹性地应对应用程序和服务中的故障。

更大的服务或解决方案应该正式地将每个功能划分到一个或多个单独的数据库中。例如,如果某个服务具有将邮件发送给客户以便注册该服务的功能,则可能需要将用于此功能的数据与该服务中的其他数据隔离开来,甚至可能需要跨多个数据库分摊邮件数据,以便处理高负载的情况(如果负载超过单台计算机的处理能力)。有可能需要多组不同的数据库,每组数据库支持不同的核心功能,以便在规模以及可用性方面管理计算机的容量限制。

因为在 Windows Azure 上设计大型解决方案的方式中存在着重要差异,所以,用来评估 SQL Server 解决方案的“性能”的定义可能要做调整。如果在购买 SAN 后 SQL Server 解决方案的性能提高了,则当 SQL Database 中没有 SAN 时,I/O 延迟的性能指数可能不好。尽管通常可以满足大多数相同的核心业务要求而不出问题,您不应将评估硬件的原始性能作为确定 SQL Database 是否可以解决特定业务问题的方法。硬件虽然较慢,但是您通常可以使用多个计算机来实现相同的业务结果和达到所需的性能目标。

SQL Database 所具有的功能集与 SQL Server 并不完全相同。SQL Database 实例是编程概念 – 举例来说,它们表示逻辑数据库而非服务器上的某个特定实例。此外,SQL Server 中的某些功能已被 SQL Database 的编程外围应用阻止。例如,SQL Database 当前不公开文件组或数据库内分区,因为标准 SQL Database 编程范例不需要它们。此外,当前也尚未公开一些与高端数据仓库(列存储索引)有关的用例。这些差异使 SQL Database 可对托管模型进行一系列革新,假以时日,通过其他方法 SQL Database 也将同样可以为客户提供其中大部分的功能优势。

SQL Database 还力争降低与运行、管理和优化数据库有关的人工成本。为此,它提供自动机制来处理一些定期执行但是很难执行到位的任务,如:

  • 数据库备份(每个活动数据库每隔几分钟就执行一次)

  • 高可用性(SQL Database 通过为每个数据库提供至少三个副本实现此功能,无需任何用户操作)

  • 数据库一致性检查

  • 自动升级 – SQL Database 将以近乎透明的方式自动升级到服务的新版本。

这些差异意味着每个计算机的一部分容量被预留用于执行这些操作。这影响了可用于确定系统的高峰性能限值的方法,我们将在下面详细讨论。明确定义每个业务操作的性能目标,有助于将性能评估与这些自动机制的影响区分开。

Windows Azure 使用户可以构建面向 Internet 的超大型服务。尽管这不完全是 Windows Azure 所特有的能力,但是使用 SQL Server 构建此类解决方案需要非常高的专业知识和雄厚的资金支持,从而使得此类应用程序常常成为一般企业遥不可及的奢望。这些高端解决方案通常也需要采取措施,通过设计代码来避免数据库或其他组件崩溃,以减小或消除解决方案或服务的停机时间。例如,无停机解决方案就应该包含某些高可用性 (HA) 解决方案(如数据库镜像或 Always-On),避免硬件故障和合理安排升级以尽量减少服务器停机时间。此方法对于基于 SQL Database 的解决方案至关重要,因为应用程序的很大一部分是服务,而新的客户群正在尝试在这个新平台上构建这类完全联机的解决方案。

SQL Database 已为您提供了内置在核心解决方案中的高可用性。处理内部升级到 SQL Database 服务导致的停机时间所用的重试逻辑与处理与商用硬件有关的故障所用的重试逻辑相同。但是,客户服务还必须考虑如何跨应用程序的多个层合理安排升级过程。例如,如果服务包含位于数据层上的 Web 或应用程序层,通常使用以下方法来升级:

  • 将新的存储过程与现有存储过程并行部署 (sp_createaccount_v1, sp_createaccount_v2)

  • 升级应用程序层计算机(使用 Windows Azure 中的“VIP 交换”方法)来切换到应用程序的新版本。如果 VIP 交换对相关服务有影响(例如缓存),可以使用就地升级方法。

  • 在存储过程的“v1”版本上完成现有工作

  • 完成所有以前的工作后,应用删除存储过程“v1”版本的另一个更改

通过将应用程序升级拆分为无需停机执行的一些小步骤,可以高效联机执行应用程序升级。如果一些步骤需要停机,应采取措施尽量减少停机时间,例如一次安排少量内部客户进行这些步骤(在这些客户的空闲时间),或在后台将用户数据复制到新数据库,使他们根本察觉不到总体服务“停机”。

这种心理变化要求在工程设计方面进行一些更改,但是拆分还允许您考虑采用其他一些解决方法,例如仅仅停止服务的一部分以便进行升级,而不用停止整个服务。例如,在升级期间,您可以禁用显示客户前几个月帐单的逻辑,但是保持主服务运行。处理商用硬件所需的拆分还可以更精确地重新定义和最小化客户可见的停机影响。

客户需要确定在升级周期中是否必须满足性能目标。如果必须满足,则应设置服务/应用程序来处理服务自身部署所导致的任何额外负载或中断。

很多客户在本地子网上部署 SQL Server 和解决方案的所有其他组件,这意味着层之间的网络延迟通常可以忽略不计。当您将解决方案移到托管的基础结构时,可能增加客户和解决方案某些部分之间的延迟时间。此外,Windows Azure 中的各个层并不位于同一子网,因此任意两个层之间将存在小的延迟差异。由于这些物理网络差异,对于很“烦琐”(执行很多小查询或更新)的 SQL Server 解决方案,您可能发现它们在 SQL Database 上的运行速度变慢了。对于熟悉早期客户端-服务器计算的那些用户,此处可以采用同样的解决方法:认真考虑解决方案中两个层之间的往返时间以消除所有可见的延迟差异。

以下两个实用方法可以测量此延迟的影响:

  • 实现一个简单 Web 角色或辅助角色应用程序,连续执行针对 Windows Azure SQL Database 实例的简单“SELECT 1”查询,然后跟踪开始和结束响应时间。

  • 使用远程桌面连接该 Web/辅助角色实例并执行资源监视器小程序。在“网络”选项卡中,您将发现带“延迟时间(毫秒)”列的“TCP 连接”网格,通过它可以了解与 SQL Database 连接有关的延迟情况(您可以通过以下方法来查找 Windows Azure SQL Database 虚拟服务器 IP 地址:使用全限定名称执行 pingtracert,然后查看与端口 1433 的连接)。

有关涉及延迟的最佳做法和优化的详细讨论,请参阅以下 Windows Azure 指南文章:针对 Windows Azure SQL Database 的性能注意事项

最后,SQL Database 服务本身的一些问题有时也可能影响您执行性能分析的方式。例如,SQL Database 并不公开 SQL Server 上的所有功能。Windows Azure SQL Database 中缺少的一些功能(例如 SQL Profiler)通常用于执行本地 SQL Server 的性能故障排除。此功能差异是编程外围应用的变化所导致的,它将数据库而非服务器作为 SQL Database 服务中的主要概念公开。在多数情况下,SQL 团队计划分阶段提供相同或等效的功能来克服这些限制。

可能存在的另一个问题是 SQL Database 本身有 bug。尽管 SQL Server 中的客户有自己的软件副本,软件在 SQL Database 中是共享的。偶尔我们的软件会有 bug,SQL 团队可能需要缓解问题或阻止功能直到解决问题。SQL 团队规划功能以帮助使此过程对于我们服务的客户更可见、更易于管理。由于在任何时间点运行的不同 SQL Database 群集可能有行为差异,性能评估最好在不同时间和不同数据中心中进行,以帮助隔离服务中的任何暂时性限制的影响。这些措施将从针对服务的任何单个测试结果中剔除大多数上述因素的影响。

本节介绍一些推荐的方法来验证和优化 SQL Database 解决方案并说明要避免使用的一些特定方法。

既然存在平台差异,那么对 SQL Database 执行“同类对象”比较就没有意义 -- 硬件不同,平台存在差异,所用的方法也应该不同。迁移现有解决方案时,客户使用确定核心操作需要何种性能的方法在平台上完成大部分工作,然后确定如何调整代码来满足这些要求。

请参考如下示例:

某客户具有一个本地 ASP.NET 应用程序,它对本地 SQL Server 数据库进行 10 次调用并在 2 秒内呈现页面。此 Web 应用程序计划迁移到运行 Web 或应用程序层辅助角色并使用 SQL Database 作为数据库的 Windows Azure。最初,客户决定只将应用程序移过去,然后编译它。在花费了很多时间解决了各种问题,最终将应用程序部署到 Windows Azure 后,客户发现现在呈现网页所用的时间为 4 秒(比以前慢 2 秒)。客户因此得出云可能“很慢”的结论。

可能有几个因素导致了此延迟差异(与此有关的所有特定示例请参见本主题前面的章节):

  • 调用者与 Web 服务器之间也可能有延迟(调用者距离数据中心很远,请求在 Internet 上传输需要一定时间)

  • 来自 ASP.NET 应用程序的每个 SQL 调用也会导致延迟时间略有增加,因为两个层不位于同一子网

  • 本地使用的计算机可能不同于 SQL Database 使用的计算机

  • 查询计划可能不同

同样,最重要的一点是您不应尝试比较任何现有解决方案在 SQL Server 和 SQL Database 上的性能,因为导致它们性能不同的原因有很多。我们仍需要直接关注重要的问题:解决业务问题需要什么级别的性能? 在本例中,如果客户要求在 2 秒内呈现网页,则在 Windows Azure 平台上可以采用很多方法来实现此目标(尽管实际采用的解决方案可能有所不同:它可以是本地 SQL Server 解决方案、Windows Azure 虚拟机 (IaaS) 解决方案或 SQL Database)。对于此示例,我们可能进行一些更改来针对此 2 秒目标提高性能:

  • 确定客户端和 Web 服务器之间的延迟时间(生成一个测试网页并测量无需联系数据库的网页的响应时间)。如果这是重要因素,则考虑找到一个更近的数据中心来减少延迟时间和/或考虑使用内容分发网络 (CDN),通过将内容缓存到更接近每个用户的位置来减少用户可察觉的延迟时间。

  • 考虑是否可以合并来自 ASP.NET 应用程序的往返次数以减小两个层之间相关的所有物理网络开销。这也可以缩短一点延迟时间。

  • 检查运行每个查询所用的时间,搜索可以改进的缓慢查询计划、搜索可以取消的锁定或可以增加的数据库资源。如有必要,定义每个操作的性能目标并对每个子问题重复该方法。

  • 确定在被限制前可以对数据库进行多大的压力测试(查看 master 数据库中的新 sys.event_logsys.database_connection_stats 动态管理视图),了解是否需要考虑:

    • 减少应用程序级别的连接数

    • 向外扩展数据模型,添加更多数据库来实现所要达到的吞吐量级别

关键的一点是,针对一个业务问题定义目标,而非尝试确定 SQL Database 是否具有与 SQL Server 相同的性能;这几乎肯定是不同的。

当您发现一个性能问题时,尝试将该问题与资源使用和差异所导致的任何其他问题隔离开通常很有用。继续以前面的示例为例,我们假定执行一个特定查询所需的时间为 3 秒。找到该查询并直接在数据库上运行它(可能在从 SQL Server Management Studio 调用的存储过程内部执行),以将该查询与原始解决方案隔离。可能 Web 应用程序的配置有点错误或查询计划不合理。运行隔离的测试将排除一些可能的原因并帮助快速隔离要解决的关键问题。有时,找到性能低下的操作并将它进一步拆分(例如删除某个查询文本的关键部分)更有意义,这样可以了解采取哪个措施可以避免出现性能问题,使操作速度再次变快。SQL 产品团队在帮助客户隔离问题时经常使用此方法。在连接级别添加 SET STATISTICS IO ONSET STATISTICS TIME ON 设置可以帮助提供有关执行性能的其他详细信息。

托管基础结构可能更难进行故障排除。不可能直接查看性能计数器或在本地部署中提供的其他数据。能够进行带日志记录的可重复测试非常有用(为了不影响结果,日志可能存储在不同数据库),因为该测试可以部署在不同数据中心。尽管当前每个数据中心中使用的硬件基本上相似,但是仍存在一些差异,这些差异可能反映在特定测试中,特别是对特定操作具有严格延迟时间要求的客户方案更是如此。一旦您确定指定的操作很重要,应在应用程序计划部署到的每个数据中心验证该隔离的示例。并非所有客户具有正规性能测试套件,此步骤还可以帮助进行本地验证。

在隔离的环境中,有时并不需要考虑多次运行的操作的性能。由于后台系统操作以及系统中其他租户的影响,共享环境可能具有更大的差异。因此,有必要设计可重复的测试并多次运行它。收集多次运行的结果通常可以显示不同调用的一些性能差异,您可以通过对指定的重复操作的百分位数分布作图,了解相关原因。

请参考如下示例:

一个客户查询对某个操作具有 20 毫秒的迫切延迟时间要求,运行该查询 1000 次,客户发现其平均延迟时间为 26.95 毫秒。通过查看所有调用的延迟时间百分位数分布,相关数据如下所示:

(x 轴表示运行同一查询 N 次的指定测试的百分位数。y 轴表示以毫秒为单位的延迟时间。)

有趣的是,数据分布不均匀 -- 在数据集的顶端有一个大的尖峰。显然只查看平均值无法得到正确结论。很多延迟响应曲线是指数函数,您可以通过选取曲线上的几个关键点并说明关键百分位数上的延迟时间来描绘延迟情况:

 

平均值

26.95 毫秒

第 50 百分位数

15 毫秒

第 95 百分位数

21 毫秒

第 99 百分位数

80 毫秒

第 99.9 百分位数

1044 毫秒

对于此示例,主要问题出现在数据分布的尾部。有一个偶然数据点,它远远偏离其他数据点。能以这种方式捕获并描述问题将帮助说明该问题与通常情况无关,而与几个重要但少见的情况有关。

这个指定的示例可能在共享环境中发生,它通常与解决方案中所用路径上的某个路由器上删除的 TCP 数据包有关。如果删除该数据包,TCP 将在大约 1 秒后重试。在此示例中,此问题的可能原因是网络拥堵。如果此问题出现的频率明显增加,则可能暗示网络硬件有问题(这意味着您应与 Microsoft 客户支持人员联系)或可能存在影响此结果的其他问题。

您应使用数据日志记录和揭示分布特点的统计信息来隔离问题,然后决定这些问题对业务是否至关重要。此分析针对一个真实问题(尽管上述示例是人为创建的,只是用于说明问题)。当现实中的客户遇到这类问题时,一些客户只是忽略这种反常情况而不采取任何措施(这意味着它对业务不重要,他们更改自己的要求以关注第 50 百分位数时间而非平均值),而在其他情况下(当在本地系统上工作时),客户在问题隔离后使用此方法更换硬件以解决在网络路由器中发现的实际问题。

必须调整体系结构和开发方法以适应云环境,同样地,操作习惯也必须更改。在 Azure SQL Database 中实现高效系统需要采用适合云环境的操作流程。需要修改和重新评估用于了解资源使用情况和效率以及检测错误的传统方法和技术,以确保它们在多租户环境中可以帮助进行正确的操作决策。

本节提供了一些实用的技术示例,可用于在 Windows Azure SQL Database 上获取有助于做出决策的有价值信息。这些实用示例说明如何进行故障排除、隔离和解决实际客户问题,并介绍了一些实用工具以及如何使用这些工具。

SQL Database 公开了很多动态管理视图 (DMV),这些视图可用于了解资源使用情况和识别故障情况。

当前推荐的方法有:

  • 识别资源使用者:使用数据库级别的 DMV 来识别占用资源最多的前几个资源使用者,监视查询执行情况和效率(例如,使用 sys.dm_exec_query_statssys.dm_db_missing_index_details

  • 使用 Delta 方法:定期拍摄在 SQL Database 上执行的当前请求的快照,以查看锁定、阻塞、闩锁和其他争用问题(例如,使用 sys.dm_exec_requests

  • 监视错误:监视 master 数据库 DMV 以查看与连接有关的问题,如限制(例如,使用 sys.event_logsys.database_connection_stats

  • 监视数据库资源使用量:监视 sys.resource_stats DMV 以查看一段时间内的数据库资源使用量。

一些 DMV 维护自上次移动主 Windows Azure SQL Database 以来的累积信息(例如 sys.dm_exec_query_stats)。其他 DMV 包含某个时间点的快照 (sys.dm_exec_requests)。查询的性能受以下因素影响:资源可用性、在该时间点 SQL Database 实例所在的主节点和辅助节点的并发度,以及应用程序问题(如锁定或阻塞)。在本地环境中,通常将 sys.dm_os_wait_statssys.dm_exec_query_stats 一起使用来了解查询性能和资源限制,这些信息可以从系统等待信息推断出。sys.dm_db_wait_stats 提供按在操作期间执行的线程划分的所有等待情况的聚合视图。例如,锁等待指示查询争用数据;页 IO 闩锁等待指示 IO 响应时间较慢。

还可以使用另一方法:

  • 在开始监视时拍摄查询统计信息的初始快照。将它保存在临时表或物理表中。

  • 在监视期间定期拍摄 exec 请求的快照。

  • 拍摄查询统计信息的第二个快照。

  • 比较两个快照之间的差异,使用 exec 请求信息来了解锁定、阻塞或资源等待是否影响了查询性能。

某客户遇到一个问题:客户从调用 Windows Azure SQL Database 的 Web 应用程序发出用户请求的速度很慢。

下面的脚本标识了在您的 SQL Database 实例上执行最多的批处理。此示例显示按总 CPU 时间排序的查询以标识占用 CPU 最多的查询。相同的查询模式(按不同属性排序)可以找到每种资源(逻辑 I/O、占用时间等)的其他主要使用者。在很多事务系统中,前几个结果是一致的(例如,排名靠前的聚合 CPU 使用者也占用最多的 I/O 资源)。

/* Top batches by total CPU time No execution plan
**************************************/
SELECT TOP(25)
SUBSTRING (st.text,1, 512),
SUM(qs.total_worker_time) AS total_cpu_time, 
SUM(qs.total_elapsed_time) AS total_elapsed_time,
CAST((CAST(SUM(qs.total_worker_time) AS decimal) / cast(SUM(qs.total_elapsed_time) AS decimal) * 100) AS int)  AS cpu_vs_elapsed_percentage,
SUM(qs.execution_count) AS total_execution_count,
SUM(qs.total_worker_time)/SUM(qs.execution_count) AS average_cpu_time,
SUM(qs.total_elapsed_time)/SUM(qs.execution_count) AS average_elapsed_time,
SUM(qs.total_logical_reads) AStotal_logical_reads,
SUM(qs.total_logical_reads)/SUM(qs.execution_count) AS average_logical_reads,
SUM(qs.total_logical_writes) AS total_logical_writes,
COUNT(*) AS number_of_statements,
qs.plan_handle,
db_name(st.dbid) AS  [database name],
st.objectid
FROM sys.dm_exec_query_stats AS qs
CROSS APPLY sys.dm_exec_sql_text(sql_handle) AS st
GROUP BY st.text, qs.plan_handle, db_name(st.dbid), st.objectid
ORDER BY SUM(qs.total_worker_time) DESC
   

在此示例中,存储过程 dbo.spGetTranslationTable 占用 CPU 的时间比下一个批处理多 42 倍。

要确定指定查询的资源使用率和效率,常用方法是使用 SQL Server Management Studio (SSMS) 在隔离环境中执行该查询。SET STATISTICS IOTIME 选项可用于提供有用信息,还可以获取实际执行计划。具体语句如下所示:

SET STATISTICS IO ON;
SET STATISTICS TIME ON;
exe [dbo].[spGetTranslationTable] @lang='en'

在此示例中,查询对 translations 表执行了 900 次逻辑读。通过查看查询计划,这是扫描 translations 聚集索引以返回相对较小的行子集导致的。对该表创建新索引可以减少逻辑 I/O 并解决问题。

sys.dm_exec_requests 提供有关查询性能的数据,如锁定、阻塞和等待类型。(有关示例查询,请参阅下面附录一节中“具有 WAIT TYPEWAIT TIMEsys.dm_exec_requests 的快照”)。SQL Database 中实现了一些新等待类型来表示 SQL Database 特有的功能。SE_REPL_SLOW_SECONDARY_THROTTLESE_REPLY_COMMIT_ACK 等待类型与 SQL Database 用于满足可用性目标和提供弹性的日志复制机制有关。如果这些等待数据出现在 DMV 结果中,它通常指示复制通道的 I/O 压力。缓解措施包括:

  • 最小化 DML 操作

  • 管理应用程序批插入的大小和事务大小

  • 减少应用程序插入的总次数

  • 认真管理数据库操作(如索引维护)

Tip提示
如果 SE_REPL 阻塞期间显著影响应用程序性能,请与 Microsoft 客户支持人员联系以获得帮助。

如果数据库上的负载导致 SE_REPL 等待或该数据库被 Windows Azure 限制,有不同的解决方法。对于写密集的工作负载,请考虑将数据分片以将数据分散到多个数据库。这既可以减小系统中任何一个节点的负载,也可以降低单个数据库计算机出现因限制或阻塞导致整个应用程序性能受影响的情况的风险。对于读密集的工作负载,请考虑使用分布式缓存技术(如 Windows Azure Caching)来缓存数据库的信息和降低查询它的频率。

显示 SE_REPL_SLOW_SECONDARY_THROTTLE 的快照:

显示 SE_REPL_COMMIT_ACK 的快照:

我们正在不断扩展 Windows Azure SQL Database 的外围应用以提供其他 DMV 和其他故障排除信息。SQL Database 中已启用以下与索引有关的 DMV:

  • sys.dm_db_index_usage_stats

  • sys.dm_db_missing_index_details

  • sys.dm_db_missing_index_group_stats

  • sys.dm_db_missing_index_groups

“缺失索引分析”一节(参见下面的附录)中所示的查询标识缺失索引建议,其方式与您在与 SQL Server 交互所用的方式基本相同。此信息也在执行计划中提供,您可以从计划缓存或通过 SSMS 获取执行计划。下面的快照说明了这一点。

每个 SQL Database 是虚拟服务器(即逻辑服务器)的成员。此逻辑服务器具有一个 master 数据库,现在该数据库已经扩展以提供 sys.event_logsys.database_connection_stats DMV。sys.database_connection_stats 视图显示聚合 5 分钟时间内总共有多少 TDS 连接成功、被终止或限制。sys.event_log 视图提供有关成功的 SQL Database 连接、连接失败、死锁和限制事件的详细信息。通过这些 DMV,您可以快速解决应用程序连接问题。

有关详细信息,请参阅:

http://blogs.msdn.com/b/wayneb/archive/2012/11/02/introducing-sys-database-connection-stats-for-troubleshooting-windows-azure-sql-database.aspx

http://blogs.msdn.com/b/windowsazure/archive/2012/11/05/announcing-new-dynamic-management-views-for-windows-azure-sql-database.aspx

sys.resource_stats DMV 提供一段时间内的资源使用量视图。可使用此 DMV 调查资源使用情况和为应用程序做容量规划。

要返回过去 7 天 sys.resource_stats DMV 中所有信息的查询:

SELECT * FROM sys.resource_stats
WHERE  database_name = 'MyTestDB' AND start_time > DATEADD(day, -7, GETDATE())

返回的结果的屏幕快照:

要返回过去 7 天数据库的平均和最大资源使用量的查询:

SELECT 
    avg(avg_cpu_cores_used) AS 'Average CPU Cores Used',
    max(avg_cpu_cores_used) AS 'Maximum CPU Cores Used',
    avg(avg_physical_read_iops + avg_physical_write_iops) AS 'Average Physical IOPS',
    max(avg_physical_read_iops + avg_physical_write_iops) AS 'Maximum Physical IOPS',
    avg(active_memory_used_kb / (1024.0 * 1024.0)) AS 'Average Memory Used in GB',
    max(active_memory_used_kb / (1024.0 * 1024.0)) AS 'Maximum Memory Used in GB',
    avg(active_session_count) AS 'Average # of Sessions',
    max(active_session_count) AS 'Maximum # of Sessions',
    avg(active_worker_count) AS 'Average # of Workers',
    max(active_worker_count) AS 'Maximum # of Workers'
FROM sys.resource_stats 
WHERE  database_name = 'MyTestDB' AND start_time > DATEADD(day, -7, GETDATE())

返回的结果的屏幕快照:

要确定是否使用多个内核的查询:

SELECT
(SELECT
    SUM(DATEDIFF(minute, start_time, end_time))
    FROM sys.resource_stats
    WHERE database_name = 'MyTestDB' AND 
          start_time > DATEADD(day, -7, GETDATE()) AND
          avg_cpu_cores_used > 1.0) * 1.0 / SUM(DATEDIFF(minute, start_time, end_time)
) AS percenage_more_than_1_core
FROM sys.resource_stats
WHERE database_name = 'MyTestDB' AND start_time > DATEADD(day, -7, GETDATE())

返回的结果的屏幕快照:

本节中提供的查询可用于单个数据库的故障排除和查找其限制。

/* Top statements by total CPU time w/ query plan on the statement level 
note - can be expensive to run this 
**requires** statement_level_query_plan.sql
************************************************************************/
SELECT TOP(25)
SUBSTRING(qt.text,qs.statement_start_offset/2, 
(CASE WHEN qs.statement_end_offset = -1 
THEN len(convert(nvarchar(max), qt.text)) * 2 
ELSE qs.statement_end_offset END -qs.statement_start_offset)/2) 
AS statement_text,
SUBSTRING (qt.text , 1, 512) AS batch_text,
qs.total_worker_time,
qs.total_elapsed_time,
CAST((CAST(qs.total_worker_time AS decimal) / cast(qs.total_elapsed_time AS decimal) * 100) AS int)  AS cpu_vs_elapsed_percentage,
qs.total_worker_time/qs.execution_count AS average_cpu_time,
qs.total_elapsed_time/qs.execution_count AS average_elapsed_time,
qs.total_logical_reads,
qs.execution_count, 
qs.total_logical_reads/qs.execution_count AS average_logical_reads,
db_name(qt.dbid) AS [database name],
qt.objectid,
pln.statement_plan
FROM sys.dm_exec_query_stats qs
CROSS APPLY sys.dm_exec_sql_text(qs.sql_handle) AS qt
CROSS APPLY master.dbo.statement_level_query_plan(plan_handle) AS pln
WHERE statement_text LIKE
'%' + REPLACE(LEFT(SUBSTRING((select text from master.sys.dm_exec_sql_text(sql_handle)), 
statement_start_offset/2, 1 + CASE WHEN statement_end_offset = -1 
THEN LEN((SELECT text FROM master.sys.dm_exec_sql_text(sql_handle))) - statement_start_offset/2
ELSE statement_end_offset/2 - statement_start_offset/2 
END) 
,3000), '[','[[]') + '%'
ORDER BY qs.total_worker_time DESC  

-- TOP 50 Query Plans in SQL Azure Database
SELECT TOP(50)
            CONVERT(bigint, DATEPART(yyyy,getdate())*10000000000)+(DATEPART(mm,getdate())*100000000)+(DATEPART(dd,GETDATE())*1000000)+(DATEPART(hh,GETDATE())*10000)+(DATEPART(mi,GETDATE())*100)+(DATEPART(ss,GETDATE())) AS timestampKey ,
                GETDATE() AS eventdateUTC,
            CONVERT(decimal(8,2),(CAST(qs.execution_count AS FLOAT)/(DATEDIFF(mi,qs.creation_time,qs.last_execution_time)+1))*(((CAST(qs.total_worker_time AS FLOAT) / qs.execution_count) / 100000.00)+((CAST(qs.total_elapsed_time AS float) / qs.execution_count) / 1000000.00))) AS Weighting,
            query_plan.value('declare default element namespace "http://schemas.microsoft.com/sqlserver/2004/07/showplan"; 
                            (/ShowPlanXML//Object/@Index)[1]', 'varchar(255)') AS IndexName, 
            qp.query_plan,
                    CASE WHEN query_plan.exist('
                        declare default element namespace "http://schemas.microsoft.com/sqlserver/2004/07/showplan"; 
                        /ShowPlanXML/BatchSequence/Batch/Statements//RelOp/@PhysicalOp[. = "Hash Match"]'
                               )=1 THEN 1 ELSE 0 END AS hash_match
                    , CASE WHEN query_plan.exist('
                        declare default element namespace "http://schemas.microsoft.com/sqlserver/2004/07/showplan"; 
                        /ShowPlanXML/BatchSequence/Batch/Statements//RelOp/@PhysicalOp[. = "Table Scan"]'
                               )=1 THEN 1 ELSE 0 END AS table_scan
                    , CASE WHEN query_plan.exist('
                        declare default element namespace "http://schemas.microsoft.com/sqlserver/2004/07/showplan"; 
                        /ShowPlanXML/BatchSequence/Batch/Statements//RelOp/@PhysicalOp[. = "Clustered Index Scan"]'
                               )=1 THEN 1 ELSE 0 END AS clustered_index_scan
            ,SUBSTRING(qt.text,qs.statement_start_offset/2+1, 
            (CASE WHEN qs.statement_end_offset = -1 
            THEN LEN(CONVERT(nvarchar(max), qt.text)) * 2 
            ELSE qs.statement_end_offset END -qs.statement_start_offset)/2) 
            AS statement_text,
            qs.total_worker_time/qs.execution_count AS average_cpu_time,
            qs.total_elapsed_time/qs.execution_count AS average_elapsed_time,
            qs.total_logical_reads/qs.execution_count AS average_logical_reads,
            qs.total_logical_writes/qs.execution_count AS average_logical_writes,
            qs.execution_count,
            qs.plan_generation_num,
            qs.total_worker_time,
            qs.total_elapsed_time,
            qs.total_logical_reads,
            qs.total_logical_writes,
            db_name() AS [db_name],
            qt.objectid,
            qs.query_hash, 
            qs.creation_time,
            qs.last_execution_time
            FROM sys.dm_exec_query_stats qs
            CROSS APPLY sys.dm_exec_sql_text(qs.sql_handle) AS qt
            CROSS APPLY sys.dm_exec_query_plan(qs.plan_handle) AS qp
            WHERE qt.text NOT LIKE '%sys.%'
            ORDER BY qs.total_elapsed_time DESC

-- Snapshot of current sys.dm_exec_requests with WAIT TYPE and WAIT TIME
declare @i int = 0
                    DECLARE @Requests TABLE (
                    [timestampKey] [bigint] NULL,
                    [eventdateUTC] [datetime] NOT NULL,
                    [statement_text] [nvarchar](max) NULL,
                    [command] [nvarchar](32) NOT NULL,
                    [cpu_time] [int] NOT NULL,
                    [total_elapsed_time] [int] NOT NULL,
                    [wait_type] [nvarchar](60) NULL,
                    [wait_time] [int] NOT NULL,
                    [last_wait_type] [nvarchar](60) NULL,
                    [wait_resource] [nvarchar](60) NOT NULL,
                    [reads] [bigint] NOT NULL,
                    [writes] [bigint] NOT NULL,
                    [logical_reads] [bigint] NOT NULL,
                    [row_count] [bigint] NOT NULL,
                    [granted_query_memory] [int] NOT NULL,
                    [query_hash] [binary](8) NULL,
                    [query_plan] [xml] NULL,
                    [hash_match] [bit] NULL,
                    [table_scan] [bit] NULL,
                    [clustered_index_scan] [bit] NULL,
                    [session_id] [smallint] NOT NULL,
                    [blocking_session_id] [smallint] NULL,
                    [start_time] [datetime] NOT NULL,
                    [status] [nvarchar](30) NOT NULL,
                    [db_name] [nvarchar](128) NULL
                    )

                    WHILE(@i < 20)
                    BEGIN
                    INSERT INTO @Requests
                    SELECT 
                    CONVERT(bigint, datepart(yyyy,getdate())*10000000000)+(datepart(mm,getdate())*100000000)+(datepart(dd,getdate())*1000000)+(datepart(hh,getdate())*10000)+(datepart(mi,getdate())*100)+(datepart(ss,getdate())) as timestampKey ,
                    getdate() as eventdateUTC,
                    SUBSTRING(t.text,r.statement_start_offset/2+1,(case when r.statement_end_offset = -1 then len(convert(nvarchar(max), t.text)) * 2 else r.statement_end_offset end -r.statement_start_offset)/2) as statement_text, 
                    command,
                    cpu_time,
                    total_elapsed_time,
                    wait_type,
                    wait_time,
                    last_wait_type,
                    wait_resource,
                    reads,
                    writes,
                    logical_reads,
                    row_count,
                    granted_query_memory,
                    query_hash,
                    query_plan,
                    case when query_plan.exist('
                        declare default element namespace "http://schemas.microsoft.com/sqlserver/2004/07/showplan"; 
                        /ShowPlanXML/BatchSequence/Batch/Statements//RelOp/@PhysicalOp[. = "Hash Match"]'
                               )=1 then 1 else 0 end as hash_match
                    , case when query_plan.exist('
                        declare default element namespace "http://schemas.microsoft.com/sqlserver/2004/07/showplan"; 
                        /ShowPlanXML/BatchSequence/Batch/Statements//RelOp/@PhysicalOp[. = "Table Scan"]'
                               )=1 then 1 else 0 end as table_scan
                    , case when query_plan.exist('
                        declare default element namespace "http://schemas.microsoft.com/sqlserver/2004/07/showplan"; 
                        /ShowPlanXML/BatchSequence/Batch/Statements//RelOp/@PhysicalOp[. = "Clustered Index Scan"]'
                               )=1 then 1 else 0 end as clustered_index_scan
                    ,session_id,
                    blocking_session_id,
                    start_time,
                    status,
                    db_name() as [db_name] 
                    FROM 
                    sys.dm_exec_requests r
                    cross apply sys.dm_exec_sql_text(r.sql_handle) t
                    cross apply sys.dm_exec_query_plan(r.plan_handle) p
                    WHERE @@spid<>r.session_id
                       waitfor delay '00:00:00:100'
                       set @i = @i+1
                    end
                    select * from @Requests;

-- Missing indexes stats
SELECT  CONVERT(bigint,datepart(yyyy,getdate())*10000000000)+(datepart(mm,getdate())*100000000)+(datepart(dd,getdate())*1000000)+(datepart(hh,getdate())*10000)+(datepart(mi,getdate())*100)+(datepart(ss,getdate())) [timestampKey] 
 ,getdate() [eventdateUTC], 
 db_name() as [db_name], 
 mig.index_group_handle, 
 mid.index_handle,  
 'CREATE INDEX missing_index_' + 
 CONVERT (varchar, mig.index_group_handle) + '_' + 
 CONVERT (varchar, mid.index_handle) + ' ON ' + 
 mid.statement   + ' (' + ISNULL (mid.equality_columns,'') + 
 CASE WHEN mid.equality_columns IS NOT NULL AND mid.inequality_columns IS NOT NULL 
THEN ',' 
ELSE '' 
 END + ISNULL (mid.inequality_columns, '') + ')' + 
 ISNULL (' INCLUDE (' + mid.included_columns + ')', '') 
 AS create_index_statement,  
 migs.*, 
 mid.database_id, 
 mid.[object_id] 
FROM sys.dm_db_missing_index_groups mig 
INNER JOIN sys.dm_db_missing_index_group_stats migs ON migs.group_handle = mig.index_group_handle 
INNER JOIN sys.dm_db_missing_index_details mid ON mig.index_handle = mid.index_handle 
WHERE 
 CONVERT(decimal (28,1), migs.avg_total_user_cost * migs.avg_user_impact * (migs.user_seeks + migs.user_scans)) > 10 
ORDER BY 
 migs.avg_total_user_cost * migs.avg_user_impact * (migs.user_seeks + migs.user_scans) DESC

另请参阅

Microsoft 正在进行一项网上调查,以了解您对 MSDN 网站的意见。 如果您选择参加,我们将会在您离开 MSDN 网站时向您显示该网上调查。

是否要参加?
显示:
© 2014 Microsoft