35

我有一台服务器托管我的网站,该网站的流量几乎为零。
每天都有一些人(< 20 人)进入该网站,并且一些 RSS 阅读器订阅了我们发布的一些提要。

几乎每天晚上,一个 RSS 阅读器都会在半夜打到我们,并得到一个异常,即网站由于连接超时而无法连接到 SQL Server。细节非常奇怪,所以我正在寻找可能是什么问题的帮助,因为我不知道从哪里开始寻找了。

我们在 Windows Server 2008 上使用 ASP.Net MVC、Entity Framework 和 SQL Server 2008。这台机器是我们从不完全是顶级供应商那里获得的专用机器,因此可能配置不理想,或者谁知道什么别的。
盒子也很小,只有 1Gb RAM,但它应该能承受我们现在的负载......

我正在复制下面的完整调用堆栈,但首先,我们知道一些事情:

  • 当 iTunes 查询我们的网站时,总是会发生错误。我相信这应该与任何事情无关,但事实是我们只能从 iTunes 获得它。我最好的猜测是,发生这种情况是因为只有 iTunes 在晚上没有其他人打我们的时候查询我们。
  • 我们的理论之一是 SQL Server 和 IIS 正在争夺内存,其中一个因未使用而被分页到磁盘,当有人“唤醒它”时,从磁盘读取所有内容需要很长时间进入记忆。这是可能发生的事情吗?(我有点放弃这个,因为如果可能的话,这听起来像是 SQL Server 中的一个设计问题)
  • 我还考虑了我们泄漏连接的可能性,因为我们可能没有适当地处理 EF 实体(请参阅我的问题here)。这是我通过谷歌搜索问题唯一能找到的。考虑到我们的负载极低,我放弃了这个。
  • 这总是在晚上发生,所以很可能与一段时间内什么都没有发生有关。例如,我很确定当这些请求命中时,Web 服务器进程被回收,它正在启动/重新 JIT 处理所有内容。但是,重新 JITting 并没有解释 SQL 超时。

更新:我们按照建议附加了一个分析器,我们花了很长时间才发现一个新的异常。这是我们知道的新东西:

  • 附加分析器极大地减少了我们得到的错误数量。事实上,在正常情况下每天获得几个之后,我们不得不等待 3 或 4 天才能发生一次。一旦我们停止分析器,它就会回到正常的错误频率(甚至更糟)。所以 profiler 有一些效果,在一定程度上隐藏了这个问题,但不是完全隐藏。
  • 查看 IIS 请求日志旁边的探查器跟踪,请求和查询之间存在预期的 1-1 对应关系。但是,时不时地,我会看到很多正在执行的查询与 IIS 日志完全没有关联。事实上,在记录实际错误之前,我在 3 分钟内收到了 750 个查询,所有这些都与 IIS 日志完全无关。查询文本看起来像 EF 生成的那种不可读的废话,它们并不完全相同,它们看起来都像来自网站的查询:相同的应用程序名称、用户等。让我们知道这有多荒谬也就是说,该站点在2 天内收到了大约 370 个访问数据库的 IIS 请求
  • 这些无法解释的查询与之前的网站查询并非来自相同的 ClientProcessID,尽管如果进程在此期间被回收,它们可能仍然来自网站。在最后一个解释的查询和第一个无法解释的查询之间几乎有一个小时没有活动。
  • 我不知道它们来自哪里的这些长串查询之一就在我记录错误之前出现,所以我相信这是我们应该遵循的线索。
  • 正如我最初预期的那样,当引发错误的查询被执行时,它来自与前一个不同的 ClientProcessID,(比前一个无法解释的晚 8 分钟,比前一个 IIS 晚几乎一小时)。这意味着,对我来说,工作进程确实已经被回收了。
  • 这是我完全不明白的。IIS 日志显示,在错误请求前一分钟,有 4 个得到了完美的服务,尽管这些查询根本没有显示在跟踪中。事实上,在这 4 个进展顺利之后,我连续快速抛出了 4 个异常,这 4 个也没有出现在跟踪中(这是有道理的,因为如果连接中有超时,则查询应该永远不会被执行,但我也没有在跟踪中看到连接尝试)

所以,简而言之,我对此一无所知。我找不到那些快速连续运行的数百个查询的原因,但我相信这些肯定与问题有关。
我也不知道如何诊断连接问题...
或者 Profiler 跟踪可能会丢失一些根据 IIS 正常执行的查询...

有任何想法吗?


这是异常信息:

System.Data.SqlClient.SqlException: Timeout expired. The timeout period elapsed prior to completion of the operation or the server is not responding.

System.Data.EntityException: The underlying provider failed on Open. ---> System.Data.SqlClient.SqlException: Timeout expired.  The timeout period elapsed prior to completion of the operation or the server is not responding.
   at System.Data.ProviderBase.DbConnectionPool.GetConnection(DbConnection owningObject)
   at System.Data.ProviderBase.DbConnectionFactory.GetConnection(DbConnection owningConnection)
   at System.Data.ProviderBase.DbConnectionClosed.OpenConnection(DbConnection outerConnection, DbConnectionFactory connectionFactory)
   at System.Data.ProviderBase.DbConnectionClosed.OpenConnection(DbConnection outerConnection, DbConnectionFactory connectionFactory)
   at System.Data.SqlClient.SqlConnection.Open()
   at System.Data.EntityClient.EntityConnection.OpenStoreConnectionIf(Boolean openCondition, DbConnection storeConnectionToOpen, DbConnection originalConnection, String exceptionCode, String attemptedOperation, Boolean& closeStoreConnectionOnFailure)
   at System.Data.EntityClient.EntityConnection.OpenStoreConnectionIf(Boolean openCondition, DbConnection storeConnectionToOpen, DbConnection originalConnection, String exceptionCode, String attemptedOperation, Boolean& closeStoreConnectionOnFailure)
   --- End of inner exception stack trace ---
   at System.Data.EntityClient.EntityConnection.OpenStoreConnectionIf(Boolean openCondition, DbConnection storeConnectionToOpen, DbConnection originalConnection, String exceptionCode, String attemptedOperation, Boolean& closeStoreConnectionOnFailure)
   at System.Data.EntityClient.EntityConnection.Open()
   at System.Data.Objects.ObjectContext.EnsureConnection()
   at System.Data.Objects.ObjectQuery`1.GetResults(Nullable`1 forMergeOption)
   at System.Data.Objects.ObjectQuery`1.System.Collections.Generic.IEnumerable<T>.GetEnumerator()
   at System.Linq.Enumerable.FirstOrDefault[TSource](IEnumerable`1 source)
   at System.Data.Objects.ELinq.ObjectQueryProvider.<GetElementFunction>b__1[TResult](IEnumerable`1 sequence)
   at System.Data.Objects.ELinq.ObjectQueryProvider.ExecuteSingle[TResult](IEnumerable`1 query, Expression queryRoot)
   at System.Data.Objects.ELinq.ObjectQueryProvider.System.Linq.IQueryProvider.Execute[S](Expression expression)
   at System.Linq.Queryable.FirstOrDefault[TSource](IQueryable`1 source)
   at MyProject.Controllers.SitesController.Feed(Int32 id) in C:\...\Controller.cs:line 38
   at lambda_method(ExecutionScope , ControllerBase , Object[] )
   at System.Web.Mvc.ReflectedActionDescriptor.Execute(ControllerContext controllerContext, IDictionary`2 parameters)
   at System.Web.Mvc.ControllerActionInvoker.InvokeActionMethod(ControllerContext controllerContext, ActionDescriptor actionDescriptor, IDictionary`2 parameters)
   at System.Web.Mvc.ControllerActionInvoker.<>c__DisplayClassa.<InvokeActionMethodWithFilters>b__7()
   at System.Web.Mvc.ControllerActionInvoker.InvokeActionMethodFilter(IActionFilter filter, ActionExecutingContext preContext, Func`1 continuation)
   at System.Web.Mvc.ControllerActionInvoker.InvokeActionMethodWithFilters(ControllerContext controllerContext, IList`1 filters, ActionDescriptor actionDescriptor, IDictionary`2 parameters)
   at System.Web.Mvc.ControllerActionInvoker.InvokeAction(ControllerContext controllerContext, String actionName)
   at System.Web.Mvc.Controller.ExecuteCore()
   at System.Web.Mvc.MvcHandler.ProcessRequest(HttpContextBase httpContext)
   at System.Web.HttpApplication.CallHandlerExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute()
   at System.Web.HttpApplication.ExecuteStep(IExecutionStep step, Boolean& completedSynchronously)

任何想法将不胜感激。

4

4 回答 4

56

内存不足

这很可能是内存问题,可能是由其他事情加重或触发的,但本质上仍然是内存问题。还有另外两种(不太可能)的可能性,您应该首先检查并消除(因为这样做很容易):

易于检查的可能性:

  1. 您可能启用了“自动关闭”:自动关闭可以完全具有这种行为,但它很少被打开。要检查这一点,请在 SSMS 中右键单击您的应用程序数据库,选择“属性”,然后选择“选项”窗格。查看“自动关闭”条目并确保将其设置为 False。还要检查 tempdb。

  2. SQL 代理作业可能导致它:检查代理的历史日志以查看在事件期间是否有任何作业持续运行。记住也要检查维护作业,因为像重建索引这样的事情在运行时经常被认为是性能问题。这些现在不太可能成为候选对象,只是因为它们通常不会受到 Profiler 的影响。

为什么它看起来像内存问题:

如果这些没有显示任何内容,那么您应该检查内存问题。我怀疑内存是您案件的原因,因为:

  • 您有 1 GB 的内存:虽然这在技术上高于 SQL Server 的最低要求,但远低于 SQL Server 的推荐值,并且远低于我的经验对于生产来说是可以接受的,即使对于负载较轻的服务器也是如此。

  • 您在同一个盒子上运行 IIS 和 SQL Server:这本身不推荐,很大程度上是因为内存争用导致,但只有 1 GB 的内存会导致 IIS、应用程序、SQL Server、操作系统和任何其他任务和/或维护都在争夺很少的内存。Windows 管理这一点的方式是通过积极地将内存从非活动进程中取出来为活动进程提供内存。在这种情况下,像 SQL Server 这样的大型进程可能需要数秒甚至数分钟才能取回足够的内存以完全处理请求。

  • Profiler 解决了 90% 的问题:这是一个很大的线索,表明内存可能是问题所在,因为通常情况下,像 Profiler 这样的东西对这个特定问题有这种影响:Profiler 任务使 SQL Server 保持一点点活跃的时间。通常,这足以将其排除在操作系统的“清道夫”列表之外,或者至少在一定程度上减少它的影响。

如何检查内存是罪魁祸首:

  1. 关闭 Profiler:它对问题有海森堡效应,所以你必须关闭它,否则你将无法可靠地看到问题。

  2. 从另一个机器运行系统监视器 (perfmon.exe),远程连接到运行 SQL Server 和 IIS 的机器上的性能收集服务。您可以通过首先删除三个默认统计信息(它们仅是本地的),然后添加所需的统计信息(如下)来最轻松地做到这一点,但请确保在第一个下拉列表中更改计算机名称以连接到您的 SQL盒子。

  3. 通过在 perfmon 上创建“计数器日志”将收集的数据发送到文件。如果您对此不熟悉,那么最简单的做法可能是将数据收集到一个制表符或逗号分隔的文件中,您可以使用 Excel 打开该文件进行分析。

  4. 设置您的 perfmon 以收集到一个文件,并向其中添加以下计数器:

    -- 处理器\%处理器时间[总]

    -- PhysicalDisk\% 空闲时间[每个磁盘]

    -- 物理磁盘\平均。磁盘队列长度[每个磁盘]

    -- 内存\页数/秒

    -- 内存\页面读取/秒

    -- 内存\可用 MBytes

    -- Network Interface\Bytes Total/sec[对于每个正在使用的接口]

    -- 进程\% 处理器时间[见下文]

    -- 进程\页面错误/秒[见下文]

    -- 流程\工作集 [见下文]

  5. 对于进程计数器(上图),您希望包括 sqlserver.exe 进程、任何 IIS 进程和任何稳定的应用程序进程。请注意,这仅适用于“稳定”流程。根据需要不断重新创建的流程无法以这种方式捕获,因为无法在它们存在之前指定它们。

  6. 在问题最常发生的时间将此集合运行到文件中。将收集间隔设置为接近 10-15 秒。(这会收集大量数据,但您将需要此分辨率来挑选单独的事件)。

  7. 发生一个或多个事件后,停止收集,然后使用 Excel 打开收集的数据文件。您可能必须重新格式化时间戳列,使其可见并显示小时、分钟和秒。使用您的 IIS 日志查找事件的确切时间,然后查看性能数据以了解事件之前和之后发生的情况。特别是你想看看它的工作集之前是否很小,之后是否很大,中间有很多页面错误。这是这个问题最明显的迹象。

解决方案:

要么将 IIS 和 SQL Server 分开到两个不同的盒子(首选),要么向盒子添加更多内存。我认为至少应该有 3-4 GB。

那个奇怪的 EF 东西怎么样?

这里的问题是它很可能是外围问题或仅对您的主要问题有贡献。请记住,Profiler 使您 90% 的事件消失了,所以剩下的可能是另一个问题,或者它可能只是问题的最极端恶化因素。由于它的行为,我猜它要么循环缓存,要么对应用程序服务器进程进行其他后台维护。

于 2009-09-29T16:21:28.653 回答
1

我会将超时的时间戳与每晚备份的执行时间进行比较。如果它们一致,您可以将您的 RSS 提要设置为当时的静态。

另一件要尝试的事情(即使它不完全是一个答案)是在遇到超时异常时立即运行sp_who 。它不会捕获所有内容(有问题的过程可能会在您运行此程序时完成),但您可能会很幸运。

您还可以在晚上回家时启动 SQL Profiler,如果您再次看到错误,则在第二天早上逐步完成活动。请确保不要从服务器本身运行它(我很确定它在启动时会提醒您这一点)。

编辑: 解决您的更新。

EF 是否更新/创建其缓存?它可以一次解释大量查询,以及为什么以后没有查询有数据库命中。

除此之外,您似乎有一只海森虫。我能想到的唯一要添加的是更多的日志记录(到文件或事件日志)。

于 2009-09-14T14:43:43.930 回答
0

它闻起来是同时运行的 cronned 东西。正如 RBarryYoung 所说.. 一些夜间备份或者它可能是别的东西 你有对服务器的 root 访问权限吗?你能看到 crontab 吗?

可能是 SQL 服务器之上的某个全文索引插件在您遇到问题时运行其重新索引过程吗?

于 2009-09-29T15:21:03.110 回答
0

就我而言,当我安装 sqlserver 2008 r2 sp3 时,问题就消失了。

服务器:Windows 7+SqlServer 2008 R2(开发者版)客户端:Raspberrypi 3B+,Asp.net Core+EF Core

于 2018-12-06T04:58:58.127 回答