故障排除基于 ASP.NET 的企业应用程序性能问题
在本文中,我将介绍一个用于故障排除基于 ASP.NET 的企业应用程序性能问题的过程和相关技能。这个过程是我在这个领域个人经验的总结。
下载数据库脚本
引言
如何查找和解决基于 ASP.NET 的企业应用程序的性能问题已经讨论了很多次。在这里,我想从一个不同的角度来谈谈这个话题。
性能问题的可能原因
性能问题可能有很多不同的原因,例如硬件性能低下、软件设计不佳、网络连接缓慢、缺少表索引等等。在这里,我将只关注数据库和应用程序,因为它们具有最多的动态因素,并且可以由软件开发人员改进或调整。
数据库
企业应用程序通常使用关系数据库作为后端来存储业务数据。由于大多数企业应用程序不处理海量数据,也不在非常复杂的环境中为客户端提供服务,因此我们在这里不需要讨论高级性能主题,例如数据分布、数据分区、高并发等。来自数据库的性能问题通常与表设计、索引和查询有关。
表设计
在大多数情况下,表应设计为达到第三范式或第四范式。但是,更高的范式可能导致查询中复杂的表连接,因此您需要为了在某些区域获得性能而牺牲一点范式。关于表设计的另一件事是字段类型,例如,当 int 不必时,您应该使用 smallint 而不是 int。通过给出合适的字段类型,您将获得更好的数据类型检查和更小的数据占用空间。关于表设计的另一件事是主键和外键。通过正确地分配主键和外键,不仅可以保证数据关系和数据完整性,还可以帮助查询引擎选择正确的执行计划。
目录
索引定义在表上,但因为它对数据库性能至关重要,所以我希望将它与表设计分开讨论。有许多类型的索引。最常用的两种索引类型是聚集索引和非聚集索引。聚集索引会强制数据库根据聚集索引字段(复数)存储数据。如果未定义聚集索引,表数据将存储在堆结构中。要检索特定记录,将执行表扫描。定义聚集索引后(一个表只能定义一个聚集索引),数据将存储在 B 树结构中。使用二分查找算法可以大大提高数据检索操作的效率。通常,聚集索引定义在主键上,因为主键字段是唯一的,并且很可能是一个整数类型。将其用于组织 B 树中的数据更有效。仅有聚集索引是不够的,我们需要添加许多非聚集索引来处理各种情况。合适的索引可以手动根据经验添加,也可以由查询优化器自动生成,例如 SQL Server 查询优化器。定义了正确的索引还不够,因为数据库查询引擎会查看统计信息来决定如果有多个可用索引时应该使用哪个索引。因此,保持数据库统计信息最新也非常重要。
查询
在您拥有了不错的表设计和正确的索引之后。下一个与数据库性能相关的关键因素是查询本身。SQL(结构化查询语言)是每个数据库平台使用的标准语言。然而,一个写得好的 SQL 与一个写得不好的 SQL 相比,性能差异可能很大。基本 SQL 语法始终是首选,而不是高级 SQL 语法,例如,为了选择数据,优先使用子查询而不是用户定义函数。是否使用内连接、外连接,或 WHERE 子句中的字段及其顺序都会影响性能。
Application
应用程序的设计有多好也决定了系统性能有多好。特别是对于直接处理数据的算法,如果应用程序以最有效的方式处理数据,那么系统性能就会更好。反之,如果应用程序处理数据效率不高,那么系统就会滞后。
性能故障排除
如何判断是否存在性能问题
对于 Web 应用程序,您可以通过非常科学的方法收集应用程序性能指标,并使用工具来显示应用程序存在性能问题,或者凭经验判断,当一个网页加载时间超过 5 秒时,就存在潜在的性能问题。
分析网页加载时间
有许多工具可以用来测量网页加载所需的时间。Fiddler、Internet Explorer Developer Toolbar 和 Firebug 是三个常用的工具。
Fiddler
Fiddler 是一个 Web 调试代理,它记录客户端和 Web 服务器之间的所有 HTTP(S) 流量。它是免费的,可以从 www.fiddler2.com 下载。
Internet Explorer Developer Toolbar
原始的 Internet Explorer Developer Toolbar 是一个单独的安装程序,必须单独下载和安装。在 Internet Explorer 8.0 之后,它成为默认组件的一部分。最初,Internet Explorer Developer Toolbar 不支持网络流量分析,后来版本才添加了该功能。
Firebug
Firebug 是一个 Web 开发工具,有助于调试、编辑和监控网站的 CSS、HTML、DOM、XHR 和 JavaScript。它是 Firefox 浏览器的可选附加组件。需要显式安装。
如何找到性能问题的根源
确定了 Web 性能问题后,下一步是找出性能问题的具体原因。
分析数据库活动
我的实践是首先分析数据库性能,以确定性能问题是否来自数据库。对于 SQL Server 数据库,我们可以使用 SQL Server Profiler 捕获到特定数据库的所有数据库活动,并查看哪些活动看起来可疑。SQL Server Profiler 是 SQL Server 数据库工具的一部分。
分析 SQL 语句
如果在 SQL Server Profiler 中发现了一个可疑的、运行时间过长的活动,那么我们可以将该活动的查询复制出来,放入 SQL Server Management Studio 中进行进一步分析。
当查询在 SQL Server Management Studio 中运行时,查看其实际执行计划是了解查询如何执行的最佳方式。
如果在 SQL Server Profiler 中没有发现可疑的长时间运行活动,那么性能问题很可能来自应用程序。
捕获内存转储
为了确定应用程序的哪个部分运行时间过长,我们通常可以使用内存转储工具 DebugDiag,在我们感觉应用程序卡顿时捕获几个内存转储。
分析内存转储
完成内存转储后,可以将转储的内存文件加载回 DebugDiag 中,并用于生成内存转储报告。从内存转储报告中,我们可以轻松地看到哪个函数被执行了,因此,我们可以回到源代码中找到该函数,并分析它为什么运行时间这么长,或者在提供相似环境的单元测试中测试该代码段,看看是否能在开发环境中重现长时间运行的情况。
在数据库和应用程序中均未发现问题
如果数据库和应用程序中都未发现问题,那么很不幸,性能问题的根源超出了软件开发人员的知识领域,因此我们需要寻求系统管理员或网络工程师的帮助进行进一步的故障排除。
实验
让我们通过两个实验来实际演示如何进行性能问题故障排除。一个性能问题来自数据库,另一个性能问题来自应用程序。
演示应用程序
演示应用程序使用 ASP.NET MVC 4 创建。它有两个演示页面:Slow Page(慢速页面)和 Slow Database Page(慢速数据库页面)。Slow Page 在应用程序方面存在性能问题,而 Slow Database Page 在数据库方面存在性能问题。
注意:演示应用程序包含不切实际的代码逻辑和 SQL 语句,仅用于演示目的。
故障排除数据库性能问题
Slow Database Page 存在性能问题。这可以用 Fiddler 来验证。
1. 运行 Fiddler。
Fiddler 默认启动时处于运行状态。它会尝试捕获所有 HTTP(S) 流量。
2. 点击 Slow Database Page 链接将其打开。
3. 在 Fiddler Web Session 面板中,您应该会看到一个 URL 为 /Home/SlowDatabasePage 的条目。(注意:您可以停止 Fiddler 捕获,以防止捕获其他 HTTP(S) 活动)
4. 通过选择捕获的 Web 会话条目,您可以看到渲染此页面所需的总时间(它用红色框突出显示)。
此页面加载了超过 15 秒。由此,我们可以得出结论:Slow Database Page 存在性能问题。
在确定了性能问题之后,下一步是使用 SQL Server Profiler 来分析数据库活动。
1. 打开 SQL Server Profiler。
2. 启动一个新跟踪,以监视 PerformanceDemo 数据库上发生的任何数据库活动。(注意:PerformanceDemo 数据库是我们的演示应用程序在后端使用的数据库。)
3. 重新加载 Slow Database Page。
4. SQL Server Profiler 中捕获了一些数据库活动。
停止分析器,以防止捕获任何进一步的数据库活动。
5. 逐条查看分析器中的记录,并找到 Duration(持续时间)数字最大的那条。
有一个查询花费了 10906 毫秒,即 10.906 秒才能完成。这告诉我们该查询存在性能问题。
6. 通过突出显示记录,您可以在底部面板中看到完整的 SQL 语句。从 SQL Server Profiler 中选择并复制 SQL 语句到 SQL Server Management Studio 中进行进一步分析。(注意:为演示目的,SQL 语句的写法非常错误)
7. 通过转到 Query -> Include Actual Execution Plan 来开启实际执行计划,然后再次执行 SQL。
底部面板中会显示一个 Execution Plan(执行计划)选项卡。选择 Execution plan 选项卡后,您可以看到 SQL Server 查询引擎如何执行此查询。
如果您研究执行计划,您会发现查询正在执行表扫描和其他低效操作。
根据我们在执行计划中发现的内容,我们可以通过在 Person 表上添加聚集索引来优化查询,以避免表扫描。
故障排除应用程序性能问题
在走完故障排除数据库性能问题的步骤后,让我们来看看如何故障排除应用程序性能问题。我们仍然可以使用上面的演示应用程序。Slow Page 也存在性能问题,这可以通过 Fiddler 确认。
1. 运行 Fiddler。
2. 点击 Slow Page 链接将其打开。
3. 在 Fiddler Web Session 面板中,您应该会看到一个 URL 为 /Home/SlowPage 的条目。
4. 通过选择捕获的 Web 会话条目,您可以看到渲染页面所需的时间。
页面加载时间超过 10 秒。好的,由此我们可以得出结论:此页面存在性能问题。
在确定了页面存在性能问题之后,下一步是使用 SQL Server Profiler 来分析数据库活动。
1. 打开 SQL Server Profiler。
2. 启动一个新跟踪,以监视 PerformanceDemo 数据库上发生的任何数据库活动。
3. 重新加载 Slow Page。
4. 回到 SQL Server Profiler 查看。没有任何内容。
由于在 SQL Server Profiler 中未发现任何问题,我们可以推断性能问题并非来自数据库。
在将数据库排除在性能问题来源之外后,我们能想到的唯一可能的性能问题来源就是应用程序。然而,问题是,如何找出是哪段代码导致了性能问题。在实际的企业 Web 应用程序中,一个网页可能被设计成在后台完成复杂的业务逻辑。通读所有源代码并不是判断性能问题来源和时间的有效方法。因此,我们真正需要的是应用程序的运行时状态。DebugDiag 是我们将要用来捕获应用程序运行时状态的工具。如果您更熟悉 WinDBG,也可以使用它。对我来说,DebugDiag 比 WinDBG 更方便、更简单,并且可以满足我们的需求。然而,在更高级的情况下,WinDBG 允许更高级的程序员单步执行源代码并在运行时评估每个变量。让我们开始使用 DebugDiag 来捕获我们演示应用程序的运行时状态。
1. 打开 DebugDiag。
2. 为特定进程添加规则。尽管 DebugDiag 提供了一个专用的规则类型来监视 IIS 中的长时间运行的 HTTP 请求。
但是,因为我们的应用程序是在 Visual Studio 开发 Web 服务器而不是 IIS 中运行,所以我们不选择那个。我们选择的是直接使用“Crash rule”(崩溃规则)类型来监视运行中的进程。
然后选择目标类型:A specific process(特定进程)。
然后选择特定进程:WebDev.WebServer40.EXE。
最后,按照向导的其余步骤,使用所有默认设置完成新规则。
3. 重新加载 Slow Page。
4. 在页面加载完成之前,快速切换回 DebugDiag 手动转储完整的内存转储。
5. 完成内存转储后。切换到 DebugDiag 的 Advanced Analysis(高级分析)选项卡,加载内存转储并开始分析,生成内存转储报告。
报告是 HTML 格式的。
通过查看报告,我们可以看到 Thread 15 是在 SlowPage action method 中运行的线程,并且其中有一个 Sleep 方法调用。
这段代码是用于演示目的,所以没有任何意义。在实际情况中,我们应该根据从调用堆栈收集到的信息来追溯到原始源代码。这很可能会给我们提供更多关于应用程序在那个区域运行缓慢的原因的线索。
摘要
基于 ASP.NET 的企业 Web 应用程序中的性能问题是一个非常常见的问题,大多数开发人员以前都遇到过,并且将来可能会再次遇到。大多数性能问题是由应用程序或数据库中的设计或编码不当引起的。遵循我上面介绍的过程,您将更有机会快速揭示性能问题的根源。让我在这里简要重复这个过程。
1. 使用 Web 调试工具确定或确认性能问题。
2. 分析数据库活动。
3. 如果发现可疑的数据库活动,请继续下一步,否则转到步骤 6。
4. 将可疑的 SQL 复制到 SQL 设计工具中进行分析。
5. 在 SQL 中找到性能问题的根源,然后我们就完成了。
6. 捕获应用程序内存转储。
7. 生成内存转储报告,并在报告中找到可疑的函数调用。
8. 回到源代码,思考代码在运行时为何卡住。
9. 如果源代码确实存在低效算法导致耗时操作,那么我们就完成了,否则,转到下一步。
10. 我们需要其他领域专家的帮助。
在大多数情况下,我们会在多个地方同时遇到应用程序和数据库的性能问题,因此我们需要一遍又一遍地重复这个过程,直到我们检测不到任何明显的慢速网页为止。此外,并非所有性能问题都可以修复或改进。当性能问题改进到无法再进一步时,就应该考虑采用替代设计来改善用户体验。例如,提供一个等待框或进度条来告知用户页面正在加载,虽然视觉提示无法加快页面加载速度,但会让用户对系统没有卡住的良好印象。本文仅粗略地触及了基于 ASP.NET 的企业 Web 应用程序性能问题故障排除的表面。软件开发人员还有更多需要探索的内容。
Using the Code
代码是在 Visual Studio 2010 中开发的。您需要先运行附加的 Database.sql 来创建 PerformanceDemo 数据库及其内部的表。然后,更新配置文件中的连接字符串以引用您的本地数据库。