我今天要来谈谈服务器,我的慢服务器端。。事实上,我的服务器端变得越来越慢,日复一日,一周又一周,我不明白为什么。。。我写这篇博文是为了让你了解我目前的情况。这是一个很长的阅读,我会尽我所能让你从理论上理解它,因为这个项目是保密的。
感谢您花时间来帮助我:)
我的SQL Server计划包含以下信息:
-
已用空间52 MB
-
分配的空间64 MB
-
最大大小250 GB
-
定价层:
Standard S0: 10 DTUs
-
应用服务计划/定价层:
Standard: 1 Small
我的发展环境如下:
项目规格和配置:
我考虑了以下情况:
我们有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
,你可能是罪魁祸首。。。
我可以提供一个小屏幕截图:
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%,并且如果发生任何并发操作,这些操作似乎都会暂停。。
如果你需要的话,我可以提供更多的细节,所以请随时索取,非常感谢你的帮助!