代码之家  ›  专栏  ›  技术社区  ›  Emixam23

不是我的API/ASP或者我的ef6sql数据库非常慢,无论是在Azure上还是在我的机器上

  •  1
  • Emixam23  · 技术社区  · 7 年前

    我今天要来谈谈服务器,我的慢服务器端。。事实上,我的服务器端变得越来越慢,日复一日,一周又一周,我不明白为什么。。。我写这篇博文是为了让你了解我目前的情况。这是一个很长的阅读,我会尽我所能让你从理论上理解它,因为这个项目是保密的。

    感谢您花时间来帮助我:)


    我的SQL Server计划包含以下信息:

    • 已用空间52 MB
    • 分配的空间64 MB
    • 最大大小250 GB
    • 定价层: Standard S0: 10 DTUs

    • 应用服务计划/定价层: Standard: 1 Small
      • 1x芯
      • 1.75 GB内存

    我的发展环境如下:

    • Win10(并行桌面13,分配8GB)

    项目规格和配置:


    我考虑了以下情况:

    我们有12个人住在9个不同的房子里。这些人有关系,我们做一个计算(不在数据库内部),计算与他们彼此之间关系的重要性/权重相关的某个分数(会话量,他们之间的联系,等等)。

    因此,在这种情况下,数据库只需通过其关系检索用户的所有连接(最多11个)。一旦找到,我们再请求数据库第二次获取每个给定用户的房子(包括它的地址、房子的详细信息)。然后,我们根据我们得到的数据进行C计算,我们把最重要的用户放在最重要的用户的位置上重新排列。

    然而,即使随着时间的推移,这个微积分可能很重,它已经需要0.8秒到2.5秒(有时需要15秒或30秒…),而且非常轻。。。

    所以我想试一下我的机器(虚拟机),它几乎是一样的。然而,假设我们在我们的情况中添加了一个事实,即用户可以更改其详细信息并编辑其房屋。好吧,性能下降得很厉害。。。即使是一个小的编辑也需要0.8到1.5。。由此,我开始认为,对API的任何调用都是缓慢的。。

    AsNoTracking() 我的林肯没有改变什么。。

    BLOCKED_TIME . 而且,当并发发生时,我可以看到 WAIT_TIME ,为什么CPU被阻塞或在等待什么?由于我没有外部需求,我在网上搜索,找到了一个完整的答案:

    https://stackoverflow.com/a/46553440/6093604


    回答 @dstj 对于 What does AWAIT_TIME exactly mean in the Azure profiler?

    Azure's documentation

    等待(等待时间)

    AWAIT_TIME 等待时间 指示等待任务完成的阻止时间+

    阻塞时间

    阻塞时间 指示代码正在等待另一个资源可用,例如等待同步对象、等待线程可用或等待请求完成。


    所以它正在等待一些必要的东西来继续处理。我们也有同样的问题 在文件上传时,结果是请求正在等待请求的流被读取( ReadAsMultiPartAsync() 为了我们。。。如果你看看 RecASPRequest _RtlUserThreadStart ,你可能是罪魁祸首。。。


    我可以提供一个小屏幕截图:

    enter image description here

    https://www.red-gate.com/simple-talk/dotnet/net-tools/entity-framework-performance-and-what-you-can-do-about-it/

    这篇文章真的很有趣,我请你读一下:)从这篇文章中,我注意到了一些重要的点:

    • 实体框架支持多个结果集,这使它能够通过单个连接向SQL Server发出和接收多个请求,从而减少往返次数。如果应用程序服务器和数据库之间存在高延迟,这一点尤其有用。只需确保连接字符串包含:

      MultipleActiveResultSets=True;

      false 在我的Azure端,默认设置为 true 在我的虚拟机上。这会是我问题的根源吗?多个实例不能同时访问数据库吗?SQL(和/或EF6)不可能/不能实现吗?

    • 变更跟踪

      当您从数据库中检索实体时,您可能会修改这些对象并期望能够将它们写回数据库。因为实体框架不知道您的意图,它必须假设您将进行修改,所以必须设置这些对象来跟踪您所做的任何更改。这会增加额外的开销,并显著增加内存需求。当检索更大的数据集时,这一问题尤其突出。

      如果您知道您只想从数据库中读取数据(例如在MVC控制器中,它只是获取要传递给视图的数据),则可以显式地告诉Entity Framework不要执行此跟踪:

      string city = "New York";
          List<School> schools = db.Schools
              .AsNoTracking()
              .Where(s => s.City == city)
              .Take(100)
              .ToList();
      

      正如我上面所说的,我已经试过了,但是从性能上来说,它目前并没有改变任何东西。当我得到更多的数据时,也许它会改变一些东西,但目前看来,跟踪并不是我性能低下问题的基础。

    • 缺少索引

      如果我完全理解这一部分,我不认为它会与我的问题有任何联系,因为索引只有在我有大量数据的情况下才会产生真正的影响,但在只有12个用户的情况下不会有影响。。。

      我们可能想找到所有住在纽约的学生。容易的:

      string city = "New York";
      var pupils = db.Pupils
          .Where(p => p.City == city)
          .OrderBy(p => p.LastName)
          .Select(x => new { x.FirstName, x.LastName })
          .ToList();
      
    • 即使我不认为下面的问题与我的问题有关,我也不明白为什么这个请求与entity看起来那么长,因为我的行为与下面的类似:

      通常我们需要基于几个条件进行搜索。例如,我们可能有一组四个搜索框供用户完成,其中空框将被忽略,因此请编写如下内容:

      //Search data as input by user
      var searchModel = new Pupil
      {
          FirstName = "Ben",
          LastName = null,
          City = null,
          PostalZipCode = null
      };
      
      List<Pupil> pupils = db.Pupils.Where(p => searchModel.FirstName)
          && (searchModel.LastName == null || p.LastName == searchModel.LastName)
          && (searchModel.City == null || p.LastName == searchModel.City)
          && (searchModel.PostalZipCode == null || p.PostalZipCode == searchModel.PostalZipCode)
          )
      .Take(100)
      .ToList();
      

      希望LastName、City和PostalZipCode子句的结果都为true,因为在本例中它们都为null,所以希望在.NET中对它们进行优化,从而留下一个与

      DECLARE @p__linq__0 NVARCHAR(20) = 'Ben'
      SELECT TOP 100
      PupilId ,
      FirstName ,
      LastName,
      etc...
      FROM dbo.Pupils
      WHERE FirstName = @p__linq__0
      

      我们很失望这不是EF查询生成的工作方式。如果我们检查实际执行的查询,如下所示:

      -- Generated by ANTS Performance Profiler
      -- Executed against .\SQL2014
      USE [EFSchoolSystem]
      DECLARE @p__linq__0 NVarChar(4000) SET @p__linq__0 = 'Ben'
      DECLARE @p__linq__1 NVarChar(4000) SET @p__linq__1 = 'Ben'
      DECLARE @p__linq__2 NVarChar(4000) SET @p__linq__2 = ''
      DECLARE @p__linq__3 NVarChar(4000) SET @p__linq__3 = ''
      DECLARE @p__linq__4 NVarChar(4000) SET @p__linq__4 = ''
      DECLARE @p__linq__5 NVarChar(4000) SET @p__linq__5 = ''
      DECLARE @p__linq__6 NVarChar(4000) SET @p__linq__6 = ''
      DECLARE @p__linq__7 NVarChar(4000) SET @p__linq__7 = ''
      -- Executed query
      SELECT TOP (100)
      [Extent1].[PupilId] AS [PupilId] ,
      [Extent1].[FirstName] AS [FirstName] ,
      [Extent1].[LastName] AS [LastName] ,
      [Extent1].[Address1] AS [Address1] ,
      [Extent1].[Adderss2] AS [Adderss2] ,
      [Extent1].[PostalZipCode] AS [PostalZipCode] ,
      [Extent1].[City] AS [City] ,
      [Extent1].[PhoneNumber] AS [PhoneNumber] ,
      [Extent1].[SchoolId] AS [SchoolId] ,
      [Extent1].[Picture] AS [Picture]
      FROM [dbo].[Pupils] AS [Extent1]
      WHERE (@p__linq__0 IS NULL OR [Extent1].[FirstName] = @p__linq__1)
      AND (@p__linq__2 IS NULL OR [Extent1].[LastName] = @p__linq__3)
      AND (@p__linq__4 IS NULL OR [Extent1].[LastName] = @p__linq__5)
      AND (@p__linq__6 IS NULL OR [Extent1].[PostalZipCode] = @p__linq__7)
      

      对于任何一个LINQ语句,都会生成一个SQL查询,并且一切都由sqlserver处理。这个查询本身看起来很混乱,但是既然这对你来说是隐藏的,那它为什么重要呢?毕竟,查询运行得很快。(请参阅以pdf格式保存的博客中的更多内容)

      基于上面的解释,我试图将我的请求拆分为多个Where,而不是只有一个条件不同的Where,结果看起来是一样的。。所以在下一部分中,我感觉到了不匹配的数据类型。

    • 数据类型很重要,如果不给予足够的关注,即使是非常简单的数据库查询也会表现出惊人的糟糕。让我们看一个例子来说明原因。我们要搜索邮政编码为90210的学生。容易的:

      string zipCode = "90210";
          var pupils = db.Pupils
          .Where(p => p.PostalZipCode == zipCode)
          .Select(x => new {x.FirstName, x.LastName})
          .ToList();
      

      不幸的是,从数据库返回结果需要很长时间。小学生表中有几百万行,但是有一个索引覆盖了正在搜索的PostalZipCode列,因此应该可以快速找到适当的行。实际上,如果我们使用

      SELECT FirstName, LastName FROM Pupils p WHERE p.PostalZipCode = ‘90210’

      让我们看看应用程序在做什么。

      类型转换:寻找转换计划隐式(nvarchar(20),[Extent1].[PostalZipCode],0)=[@p_yulinq_0]

      Data Type Precedence 比NVARCHAR。这意味着不能隐式地将宽NVARCHAR数据类型转换为较窄的VARCHAR,因为这可能会导致数据丢失(因为NVARCHAR可以表示VARCHAR不能表示的字符)。因此,要将@p_优linq_0 NVARCHAR参数与表中的VARCHAR列进行比较,SQL Server必须将索引中的每一行从VARCHAR转换为NVARCHAR。因此,它必须扫描整个索引。

      一旦你找到了,就很容易解决。您只需要编辑模型,使用列注释显式地告诉entityframework使用VARCHAR。

      public string Adderss2 { get; set; }
      [Column(TypeName = "varchar")]
      public string PostalZipCode { get; set; }
      

      在这段话之后,我尝试了两件事,第一,我的用户的年龄在你想要恢复他们的连接时有一个作用。但是,如果应用程序端出现问题,可能是用户还没有年龄,所以我使用 int? 但是,在我的 WHERE int 值。。而且,我不会强迫我 string 未来的变量 varchar nvarchar 很明显,我开始这么做,我改变了一切,我现在比较 int?.Value 致我的 内景 在我的 的条款。


    基于此,我开始想,也许我的数据库目前不是问题所在,即使我的SQL server对于一些简单的操作都达到了100%,并且如果发生任何并发操作,这些操作似乎都会暂停。。

    如果你需要的话,我可以提供更多的细节,所以请随时索取,非常感谢你的帮助!

    0 回复  |  直到 7 年前