我今天要来谈论服务器,我的服务器端很慢。事实是,我的服务器端越来越慢,日复一日,一周又一周,我不明白为什么...我写了这么多文章发布以使您处于我当前的背景/情况。这是一本很长的书,由于项目的机密性,我将尽我所能尽可能地从理论上入手。
感谢您花时间来帮助我:)
我的SQL Server计划具有以下信息:
Standard S0: 10 DTUs
我的AppPlan也具有以下详细信息:
Standard: 1 Small
我的开发环境如下:
项目的规格和配置:
在此数据库中,有静态数据和动态数据。事实是,即使我的数据库第一次通话真的很慢(没有预热),之后的通话仍然会很长。
我考虑了以下情况:
我们有12个人居住在9个不同的房屋中。这些人之间有关系,我们进行计算(不在数据库内部),计算出一定的分数,该分数与他们彼此之间的关系的重要性/权重(谈话量,他们之间通过联系建立的联系等)。
因此,在这种情况下,数据库仅需要通过其关系来检索用户的所有连接(最多11个)。一旦找到,我们便再次请求数据库来获取每个给定用户的房屋(包括其地址,房屋的详细信息)。然后,我们根据放置在最重要位置上的数据对用户进行排名来对C#进行计算。
但是,即使随着时间的推移,这种演算可能变得很沉重,它已经花费了0.8s至2.5s(有时需要15s或30s ...),而且确实很轻...
因此,我想在我的机器(VM)上进行尝试,并且几乎相同。但是,可以说,我们增加了用户可以更改其详细信息并编辑房屋的事实。好吧,性能下降太糟糕了……即使是很小的编辑也要花费0.8到1.5 ..从它开始,我开始认为对API的任何调用都是缓慢的。
但是,我也意识到并发也是一个大问题。如果我们的用户总共编辑了他们的一个信息(名字),并且同时调用了API以获取与其他用户的关系,那么一切都会变慢。.我不明白为什么,因为这仅仅是数据库中一个要编辑的单元格。另外,另一个是只读的(getRelations),所以我在我的LinQ中添加了AsNoTracking()
,它没有任何改变。
为什么首先查看数据库是因为从Azure进入“探查器”部分时,我可以看到我的数据库有许多BLOCKED_TIME
。另外,当并发发生时,我可以看到WAIT_TIME
,为什么这会导致CPU阻塞或正在等待某事?由于我没有外部需求,因此我在互联网上搜索并找到了完整的答案:
https://stackoverflow.com/a/46553440/6093604
@dstj的答案What does AWAIT_TIME exactly mean in the Azure profiler?
等待(AWAIT_TIME)
AWAIT_TIME
表示代码正在等待另一个任务完成。这通常在C#'await'语句中发生。当代码执行C#“等待”时,线程将展开并把控制权返回给线程池,并且没有任何线程被阻塞,等待“等待”完成。但是,逻辑上,等待的线程被“阻塞”,等待操作完成。AWAIT_TIME
表示等待任务完成的时间。+封锁时间
BLOCKED_TIME
表示代码正在等待另一个资源可用,例如等待同步对象,等待线程可用或等待请求完成。
因此,它正在等待继续进行处理所需的操作。我们在上传文件时遇到了
AWAIT_TIME
较长的问题,结果发现请求正在等待读取请求的流(对我们来说ReadAsMultiPartAsync()
)...如果您查看其中的代码RecASPRequest
和_RtlUserThreadStart
,您可能是罪魁祸首...
“我正在查询数据库,但某些内容不可用” ...很抱歉,什么? 我可以提供一些屏幕截图:
因为找不到任何相关信息,所以我不断在网上搜索有关Entity Framework性能的信息,并找到了该博客:https://www.red-gate.com/simple-talk/dotnet/net-tools/entity-framework-performance-and-what-you-can-do-about-it/
这篇文章真的很有趣,我邀请您阅读:)从这篇文章中,我注意到一些重要的观点:
多个结果集
实体框架支持多个结果集,这使它可以通过单个连接向SQL Server发出和接收多个请求,从而减少了往返次数。如果您的应用程序服务器和数据库之间存在高延迟,则此功能特别有用。只需确保您的连接字符串包含:
MultipleActiveResultSets=True;
但是,如果确实改变了某些东西,我不完全了解它真正改变了什么?此外,即使在我的Azure端将其设置为false
,在我的VM上也默认将其设置为true
。这会成为我问题的根源吗?不能同时由多个实例访问数据库的事实?使用SQL(和/或EF6)不可能/无法实现吗?
更改跟踪
从数据库中检索实体时,可能会修改这些对象并希望能够将它们写回到数据库中。由于Entity Framework不了解您的意图,因此必须假设您将进行修改,因此必须设置这些对象以跟踪您所做的任何更改。这增加了额外的开销,也显着增加了内存需求。在检索更大的数据集时,这尤其成问题。
如果您知道只想从数据库中读取数据(例如在MVC Controller中,只是获取数据以传递到View),则可以明确地告诉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();
泛型查询
即使我不认为以下内容与我的问题有关,我也无法理解为什么请求与实体的显示时间如此之久,因为我的行为与以下行为类似:
通常,我们希望基于几个条件进行搜索。例如,我们可能有一组四个搜索框供用户完成,其中空框被忽略,因此编写如下内容:
//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();
很希望希望将所有的结果都为true的LastName,City和PostalZipCode子句在.NET中进行优化,因为在这种情况下它们为null,因此会在…行中留下查询。
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查询,并且所有内容都由SQL Server处理。这个查询本身看起来很凌乱,但是由于您看不到它,为什么这么重要?毕竟,查询运行很快。 (请参阅另存为pdf的博客中的内容
根据上面的解释,我尝试将请求拆分为多个Where而不是仅使用一个条件不同的结果,结果似乎是相同的。因此,在下一部分中,我感觉到数据类型不匹配。 / p>
数据类型不匹配
数据类型很重要,如果对它们没有足够的重视,那么即使是简单的数据库查询,也可能执行得很差。让我们看一个例子,说明原因。我们要搜索邮政编码为90210的学生。简单:
string zipCode = "90210";
var pupils = db.Pupils
.Where(p => p.PostalZipCode == zipCode)
.Select(x => new {x.FirstName, x.LastName})
.ToList();
不幸的是,从数据库返回结果需要很长时间。 “学生”表中有几百万行,但是有一个索引覆盖着我们要搜索的PostalZipCode列,因此应该尽快找到合适的行。的确,如果我们直接使用SQL Server Management Studio查询数据库,则会立即返回结果
SELECT FirstName, LastName FROM Pupils p WHERE p.PostalZipCode = ‘90210’
让我们看看应用程序在做什么。
类型转换:CONVERT_IMPLICIT(nvarchar(20),[Extent1]。[PostalZipCode],0)= [@ p__linq__0]的寻求计划
因此[Extent1]。[PostalZipCode]被隐式转换为NVARCHAR(20)。如果我们回头看一下已运行的完整查询,则可以看到原因。实体框架已将变量声明为NVARCHAR,由于.NET中的字符串为Unicode,而NVARCHAR是可表示Unicode字符串的SQL Server类型,因此似乎很明智。
但是查看“学生”表,我们可以看到PostalZipCode列为VARCHAR(20)。为什么这是个问题?不幸的是,VARCHAR的Data Type Precedence比NVARCHAR低。这意味着无法将宽的NVARCHAR数据类型转换为较窄的VARCHAR,因为这可能会导致数据丢失(因为NVARCHAR可以表示VARCHAR不能表示的字符)。因此,为了将@ p__linq_0 NVARCHAR参数与表中的VARCHAR列进行比较,SQL Server必须将索引中的每一行都从VARCHAR转换为NVARCHAR。因此,它必须扫描整个索引。
一旦您对此进行了追踪,就很容易修复。您只需要编辑模型以使用列批注明确告诉Entity Framework使用VARCHAR。
public string Adderss2 { get; set; }
[Column(TypeName = "varchar")]
public string PostalZipCode { get; set; }
在本段之后,我尝试了两件事,首先,当您想要检索他们的连接时,我的用户的年龄具有一定的作用。但是,如果应用程序方面发生问题,则可能是用户还没有年龄,因此我使用了int?
,但是在我的WHERE
子句中,我将其进行了比较设置为int
的值。此外,我也不强制将string
变量明确地设为varchar
或nvarchar
,所以我开始这样做,我更改了所有内容, ,现在我在int?.Value
子句中将int
与WHERE
进行比较。
但是...。同样长的时间...
基于此,我开始认为,即使我的SQL服务器即使执行一些简单的操作就达到100%的速度,并且如果发生任何并发操作,这些操作似乎都已暂停,但现在我的数据库可能不是问题。
因此,我检查了服务器端的问题是什么,我不知道该从哪里开始。.我已经花了将近一个星期的时间来尝试理解所有内容(因为我不是创建该问题的人。应用程序从头开始)。我看到了一些有关Context的东西(通过API请求获得一个),依赖项注入,但是即使不确定,我也不认为这是问题所在...
有人对我应该朝哪个方向有任何想法吗?有谁知道我可以在哪里找到导致这种缓慢行为的原因的一些数据?来自Azure Portal的VS 2017 Enterprise的任何一个
我希望我能提供足够多的细节,也许是太多了,我不知道,我只是想在当前背景/情况下尽可能多地介绍你
如果需要,我可以提供更多详细信息,请随时提出,非常感谢您的帮助!
最好