11

我需要您的专业人士/专家的确认/解释,因为我的团队告诉我“没关系”,这让我很沮丧:)

背景:我们的主要 MVC3 / .Net4 Web 应用程序正在使用 SQL Server 2008。在任何给定点,我们都有大约 200 多个并发用户。服务器受到了极大的打击(锁定、超时、整体缓慢),我正在尝试应用我在整个职业生涯和最后一次 MS 认证课程中学到的东西。它们是我们都被钻研过的东西(“关闭 SQL 连接 STAT”),我试图向我的团队解释这些“小东西”,虽然不是一个单独的东西会产生影响,但最终会加起来。

我需要知道以下内容是否会对性能产生影响,或者它是否只是“最佳实践”

1.使用“USING”关键字。 他们的大部分代码是这样的:

public string SomeMethod(string x, string y) {
    SomethingDataContext dc = new SomethingDataContext();
    var x = dc.StoredProcedure(x, y);
}

虽然我试图告诉他们 USING 更快地关闭/释放资源:

using (SomethingDataContext dc = new SomethingDataContext()) {
    var x = dc.StoredProcedure(x, y);
}

他们的论点是,在代码执行完成后,GC 的清理工作已经足够好,因此 USING 不会产生巨大的影响。是真是假,为什么?

2.连接池

我一直听说设置连接池可以显着加快任何网站(至少 .Net w/MSSQL)。我建议我们将以下内容添加到 web.config 中的连接字符串中:

..."池=True;最小池大小=3;最大池大小=100;连接超时=10;"...

他们的论点是 .Net/MSSQL 已经在后台设置了连接池,不需要放入我们的 web.config 中。对或错?为什么每个其他站点都说如果已经设置了池,应该添加池以获得最佳性能?

3. 尽量减少对 DB 的调用次数

默认的 .Net MVC 项目附带的角色/成员资格提供程序非常好 - 它很方便,并且可以为您完成大部分工作。但是这些人正在认真使用UsersInRoles()并像全局变量一样自由地使用它(每次调用此方法时都会访问数据库)。我创建了一个“用户对象”,它在每次页面加载时预先加载所有角色(以及其他一些用户内容,例如 GUID 等),然后查询该对象以了解用户是否具有角色。

网站的其他部分有循环超过 200 次的 FOR 语句,每次通过执行 20-30 次 sql 查询 = 超过 4,000 次数据库调用。它以某种方式在几秒钟内完成此操作,但我想要做的是将 20-30 个 DB 调用合并为一个,以便它使 ONE 调用 200 次(每个循环)。但是因为 SQL 分析器说查询花费了“0 秒”,他们的论点是它是如此快速和小,以至于服务器可以处理这些大量的数据库查询。

我的想法是“是的,这些查询运行得很快,但它们正在扼杀整个 SQL Server 的性能。” 这可能是一个促成因素吗?我什么都不担心,还是这是导致服​​务器整体性能问题的(重要)因素?

4.其他代码优化

首先想到的是使用StringBuildervs 一个简单的字符串变量。我理解为什么我应该使用StringBuilder(尤其是在循环中),但他们说这并不重要——即使他们需要写 10k+ 行,他们的论点是性能增益并不重要。

所以总而言之,我们学习和钻研的所有东西(“最小化范围!”)只是“最佳实践”而没有真正的性能提升,还是它们都导致了真正/可测量的性能损失?

编辑*** 感谢大家的所有回答!根据您的回答,我有一个新的(第 5 个)问题:他们实际上不使用“USING”,那么这意味着什么?如果自动发生连接池,它是否会占用池中的连接,直到 GC 出现?是否有可能每个与 SQL 服务器的打开连接都给服务器增加了一点负担并减慢了它的速度?

根据您的建议,我计划对连接时间进行一些严肃的基准测试/记录,因为我怀疑 a) 服务器速度很慢,b) 他们没有关闭连接,c) Profiler 说它在 0 秒内运行,速度很慢可能来自连接。

我真的很感谢你们的帮助。再次感谢

4

9 回答 9

5

分支代码,根据当前代码库进行更改并进行基准测试+配置文件。然后,您将有一些证据来支持您的主张。

至于你的问题,这里是:

  1. 您应该始终手动处理实现的类IDisposable,GC 实际上不会调用 dispose 但是如果该类还实现了终结器,那么它将调用终结器,但是在大多数实现中它们只清理非托管资源。

  2. 确实,.NET 框架已经进行了连接池,我不确定默认值是什么,但连接字符串值会在那里允许您更改它们。

  3. SQL 语句的执行时间只是故事的一部分,在 SQL 分析器中,您将看到的只是数据库引擎执行查询所花费的时间,而您所缺少的是 Web 服务器连接到的时间并从数据库服务器接收结果,因此虽然查询可能很快,但您可以通过批处理查询节省大量 IO 和网络延迟。

  4. 这是一个很好的做一些分析来证明连接在字符串构建器上使用的额外内存的好方法。

于 2013-02-13T22:34:35.030 回答
4

哎呀。当然,您不能让 GC 为您关闭数据库连接。GC 可能不会在很长一段时间内发生……有时会在几小时后发生。一旦变量超出范围,它不会立即发生。大多数人使用 IDisposable using() { } 语法,这很棒,但至少在某些地方需要调用 connection.Close()

于 2013-02-13T22:41:39.950 回答
3
  1. 实现 IDisposable 并持有非托管资源的对象还实现了一个终结器,该终结器将确保在 GC 期间调用 dispose,问题是当它被调用时,gc 可能需要很长时间才能完成,而在此之前你可能需要这些资源. 使用完成后,立即调用 dispose。

  2. 您可以在 webconfig 中修改池化参数,但默认情况下它现在是打开的,所以如果您保留默认参数,您将一无所获

  3. 您不仅要考虑执行查询需要多长时间,还要考虑应用程序服务器和数据库之间的连接时间,即使它在同一台计算机上也会增加开销。

  4. StringBuilder 不会影响大多数 Web 应用程序的性能,只有将 2 次连接到同一个字符串时才重要,但我认为使用它是一个好主意,因为它更易于阅读。

于 2013-02-13T22:33:30.953 回答
2

我认为您在这里有两个不同的问题。

  1. 代码的性能
  2. SQL Server 数据库的性能

SQL 服务器

您是否对 SQL Server 进行了任何监控?您是否具体知道正在运行哪些导致死锁的查询?

我会阅读这篇关于死锁的文章,并考虑安装出色的Who is active以了解您的 SQL Server 中到底发生了什么。您也可以考虑安装Brent Ozar的 sp_Blitz。这应该让您对数据库中发生的事情有一个很好的了解,并为您提供首先解决该问题的工具。

其他代码问题

我无法真正评论其他代码问题。所以我会先看看SQL Server。

记住

  1. 监视器
  2. 识别问题
  3. 轮廓
  4. 使固定
  5. 转到 1
于 2013-02-13T22:37:12.900 回答
1

好吧,我不是专家,但我有一个建议:如果他们说你错了,告诉他们,“证明一下!给我写一个测试!告诉我 4000 次调用和 200 次调用一样快,并且对服务器造成同样的影响!”

其他事情同上。如果你不能让他们证明你是对的,那就证明他们是错的,用清晰的、有据可查的测试证明你说的是对的。

如果他们甚至不接受从他们自己的服务器收集的确凿证据以及他们可以查看和检查的代码,那么您可能会在该团队上浪费您的时间。

于 2013-02-13T22:34:05.950 回答
1

冒着重复这里其他人所说的话的风险,这是我对此事的 2c

首先,你应该谨慎选择你的战斗......我不会在所有 4 点上与你的同事开战,因为一旦你未能证明其中之一,就结束了,从他们的角度来看,他们是对的,而你'错了。还要记住,没有人喜欢被告知他们美丽的代码是一个丑陋的婴儿,所以我假设你会很圆滑——不要说“这很慢”,而是说“我找到了一种方法来让它更快“....(当然你的团队可能是完全合理的,所以我也是基于我自己的经验:)所以你需要从上面的 4 个领域中选择一个来首先解决。

我的钱在#3。1、2 和 4 可以有所作为,但根据我自己的经验,并没有那么大 - 但是你在 #3 中描述的对于可怜的旧服务器来说听起来像是被千张剪纸而死!查询可能执行得很快,因为它们是参数化的,所以它们被缓存了,但你需要记住,探查器中的“0 秒”可能是 900 毫秒,如果你明白我的意思的话……加起来很多事情开始变得缓慢;这也可能是锁的主要来源,因为如果这些嵌套查询中的每一个都一遍又一遍地访问同一个表,无论它运行多快,以及您提到的用户数量,您肯定会发生争用。获取 SQL 并在 SSMS 中运行它,但包括客户端统计信息,这样您不仅可以查看执行时间,还可以查看发送回客户端的数据量;这将使您更清楚地了解所涉及的开销类型。

真正证明这一切的唯一方法是像其他人提到的那样设置测试和测量,但也一定要在服务器上运行一些分析——锁、IO 队列等,这样你就可以证明您的方式不仅更快,而且它在服务器上的负载也更少。

谈到你的第 5 个问题 - 我不确定,但我猜想任何不是自动处理(通过使用)的 SqlConnection 都被视为仍然“活动”并且不再从池中可用。话虽这么说 - 服务器上的连接开销非常低,除非连接实际上在做任何事情 - 但您可以再次通过使用 SQL 性能计数器来证明这一点。

祝你好运,迫不及待地想知道你的进展如何。

于 2013-02-14T00:24:40.403 回答
0

using 子句只是语法糖,您实际上是在做

try
{
    resouce.DoStuff();
}
finally
{
     resource.Dispose()
}

当对象被垃圾回收时,Dispose 可能无论如何都会被调用,但前提是框架程序员很好地实现了可丢弃模式。所以这里反对你的同事的论点是:

i) 如果我们养成使用 using 的习惯,我们确保释放非托管资源,因为并非所有框架程序员都聪明地实现一次性模式。

ii) 是的,GC 最终会清理该对象,但可能需要一段时间,具体取决于该对象的年龄。第 2 代 GC 清理每秒只进行一次。

简而言之:

  1. 往上看

  2. 是的,池化默认设置为 true,最大池大小为 100

  3. 你是对的,绝对是推动改进的最佳领域。

  4. 过早的优化是万恶之源。首先获得#1 和#3。使用 SQL 分析器和 db 特定方法(添加索引、对其进行碎片整理、监控死锁等)。

  5. 是的,可能是。最好的方法是衡量它 - 查看性能计数器 SQLServer:一般统计信息 - 用户连接;是一篇描述如何做到这一点的文章。

始终衡量您的改进,不要在没有证据的情况下更改代码!

于 2013-02-13T22:35:38.710 回答
0

我最近正在处理我们的 Web 应用程序和我们的电子邮件提供商之间的交互中的一个错误。发送电子邮件时,发生协议错误。但不是马上。

我能够确定该错误仅在SmtpClient实例关闭时发生,这是在SmtpClient处置时发生的,这仅在垃圾收集期间发生。

而且我注意到这通常在单击“发送”按钮后需要两分钟......

不用说,代码现在正确地实现usingSmtpClientMailMessage实例的块。

对智者说一句话...

于 2013-02-13T22:45:10.213 回答
0

1 已经在上面得到了很好的解决(但是我同意它很好地处理,并且发现它是一个很好的做法)。

2 有点保留以前版本的 ODBC,其中 SQL Server 连接是独立配置的池。它曾经是非默认的;现在它是默认的。

至于 3 和 4,4 不会影响 SQL Server 的性能——StringBuilder当然,这可能有助于加快 UI 中的进程,这可能会更快地关闭 SQL 资源,但不会减少负载在 SQL Server 上。

对我来说,3 听起来是最合乎逻辑的集中注意力的地方。我尝试尽快关闭我的数据库连接,并尽可能减少调用次数。如果您正在使用LINQ,请将所有内容拉入一个IQueryable或其他内容(列表、数组等),以便您可以操作它并构建您需要的任何 UI 结构,同时在任何该hokum 之前释放连接。

综上所述,听起来您需要在探查器上花更多的时间。与其查看每次执行所花费的时间,不如查看处理器和内存使用情况。仅仅因为他们很快并不意味着他们不是“饥饿”处决。

于 2013-02-13T22:51:31.100 回答