C# 从大型数据库检索行时灵活的Linq到实体标准的性能优化
我有一个包含数十亿行的数据库。 我创建了一个函数,该函数接收用户数量的参数,并通过这些参数切割数据库。 这对我处理小数据库(30000行)效果很好,但当我尝试在大数据库上使用此函数时,我从SQLSERVER获得了C# 从大型数据库检索行时灵活的Linq到实体标准的性能优化,c#,sql-server,entity-framework,linq-to-entities,C#,Sql Server,Entity Framework,Linq To Entities,我有一个包含数十亿行的数据库。 我创建了一个函数,该函数接收用户数量的参数,并通过这些参数切割数据库。 这对我处理小数据库(30000行)效果很好,但当我尝试在大数据库上使用此函数时,我从SQLSERVER获得了TIMEOUTEXCEPTION 这是我的密码: public static IQueryable<LogViewer.EF.InternetEF.Log> ExecuteInternetGetLogsQuery(FilterCriteria p_Criteria, ref
TIMEOUTEXCEPTION
这是我的密码:
public static IQueryable<LogViewer.EF.InternetEF.Log> ExecuteInternetGetLogsQuery(FilterCriteria p_Criteria, ref GridView p_Datagrid)
{
IQueryable<LogViewer.EF.InternetEF.Log> internetQuery = null;
using (InternetDBConnectionString context = new InternetDBConnectionString())
{
internetQuery = context.Logs;
if ((p_Criteria.DateTo != null && p_Criteria.DateFrom != null))
{
internetQuery = internetQuery.Where(c => c.Timestamp >= p_Criteria.DateFrom && c.Timestamp < p_Criteria.DateTo);
}
else if (p_Criteria.DateFrom != null && p_Criteria.DateFrom > DateTime.MinValue)
{
internetQuery = internetQuery.Where(c => c.Timestamp >= p_Criteria.DateFrom);
}
else if (p_Criteria.DateTo != null && p_Criteria.DateTo > DateTime.MinValue)
{
internetQuery = internetQuery.Where(c => c.Timestamp < p_Criteria.DateTo);
}
if (!string.IsNullOrEmpty(p_Criteria.FreeText))
{
internetQuery = internetQuery.Where(c => c.FormattedMessage.Contains(p_Criteria.FreeText));
}
if (p_Criteria.Titles.Count > 0)
{
internetQuery = internetQuery.AsEnumerable().Where(c => p_Criteria.Titles.Contains(c.Title)).AsQueryable();
}
if (p_Criteria.MachineNames.Count > 0)
{
internetQuery = internetQuery.AsEnumerable().Where(c => p_Criteria.MachineNames.Contains(c.MachineName)).AsQueryable();
}
if (p_Criteria.Severities.Count > 0)
{
internetQuery = internetQuery.AsEnumerable().Where(c => p_Criteria.Severities.Contains(c.Severity)).AsQueryable();
}
internetQuery= internetQuery.OrderByDescending(c=>c.LogID);
if (internetQuery.Count() > p_Criteria.TopValue)
{
internetQuery = internetQuery.Take(p_Criteria.TopValue);
}
p_Datagrid.DataSource = internetQuery;
p_Datagrid.DataBind();
return internetQuery;
}
}
public static IQueryable ExecuteInternetGetLogsQuery(FilterCriteria p_Criteria,ref GridView p_Datagrid)
{
IQueryable internetQuery=null;
使用(InternetDBConnectionString上下文=新的InternetDBConnectionString())
{
internetQuery=context.Logs;
if((p_Criteria.DateTo!=null&&p_Criteria.DateFrom!=null))
{
internetQuery=internetQuery.Where(c=>c.Timestamp>=p_-Criteria.DateFrom&&c.TimestampDateTime.MinValue)
{
internetQuery=internetQuery.Where(c=>c.Timestamp>=p_Criteria.DateFrom);
}
else if(p_Criteria.DateTo!=null&&p_Criteria.DateTo>DateTime.MinValue)
{
internetQuery=internetQuery.Where(c=>c.Timestampc.FormattedMessage.Contains(p_Criteria.FreeText));
}
如果(p_Criteria.Titles.Count>0)
{
internetQuery=internetQuery.AsEnumerable()。其中(c=>p_Criteria.Titles.Contains(c.Title)).AsQueryable();
}
如果(p_Criteria.MachineNames.Count>0)
{
internetQuery=internetQuery.AsEnumerable()。其中(c=>p_Criteria.MachineNames.Contains(c.MachineName)).AsQueryable();
}
如果(p_标准.严重性.计数>0)
{
internetQuery=internetQuery.AsEnumerable()。其中(c=>p_Criteria.Severity.Contains(c.Severity)).AsQueryable();
}
internetQuery=internetQuery.OrderByDescending(c=>c.LogID);
if(internetQuery.Count()>p_Criteria.TopValue)
{
internetQuery=internetQuery.Take(p_Criteria.TopValue);
}
p_Datagrid.DataSource=internetQuery;
p_Datagrid.DataBind();
返回internetQuery;
}
}
我的SQL版本是2005。
我在p_Datagrid.DataBind()上遇到了一个异常代码>行
有什么建议吗?
谢谢据我所知,您有以下选项:
- 增加超时时间(坏idé只会在将来移动问题)
- 而不是执行linq查询。通过存储过程获取数据
- 制作网格页面。所以您只需检索目标页面的数据
- 查看查询计划,查看是否可以对正在执行
where
语句和orderby
的列执行任何索引
- 为什么在数据网格中需要有数十亿行。要求是什么?也许您可以只显示
TOP1000
或TOP10000
。因为从一个用户的角度来看,我看不出看到一个10亿行的网格有什么好处
那只是我的想法
编辑
如果我有这个功能,我会开始看这段代码:
if (p_Criteria.Titles.Count > 0)
{
internetQuery = internetQuery.AsEnumerable().Where(c => p_Criteria.Titles.Contains(c.Title)).AsQueryable();
}
if (p_Criteria.MachineNames.Count > 0)
{
internetQuery = internetQuery.AsEnumerable().Where(c => p_Criteria.MachineNames.Contains(c.MachineName)).AsQueryable();
}
if (p_Criteria.Severities.Count > 0)
{
internetQuery = internetQuery.AsEnumerable().Where(c => p_Criteria.Severities.Contains(c.Severity)).AsQueryable();
}
这实际上是对结果进行IEnumerable,然后对数据库调用执行内存中的where
语句。这样做也可能会有问题,因为当您调用相关表时,它会调用数据库。也许您可以获取行,然后使用IQueryable
的id执行包含的
。使用IQueryable
尿布的所有好处。由于具体方案不可用,您可以尝试以下方法
编写一个包含所有筛选条件的存储过程,并从代码中发送参数。然后从代码中执行存储过程,并检查是否仍有超时。要检查如何从实体框架调用SP,请阅读
如果第1步没有成功。您可能需要检查表设计并添加索引和/或额外的过滤器。检查有关如何为SQL Server数据库编制索引的指南的步骤
您可能还希望创建表的“卷影”副本,以保留存档的DB行。对于归档,我指的是到目前为止没有任何用处,但无法永久删除的行
编辑:我同意@Arion关于使用分页网格而不是获取所有行的观点。一般来说,像这样的“瑞士军刀”规范或标准模式很难优化(即SQL级别的索引),因为客户机/用户可以指定大量的过滤器组合排列。因此,如果您可以以某种方式强制用户指定至少一个强制标准,这将显著减少行数,例如,通过强制日期范围且不超过一个月,我将从那里开始,因为这样,当我们查看索引时,至少我们有了一些开始
由于行数可能很大,我会断言或验证用于限制行数的p_Criteria.TopValue
的值始终存在,并且是一个合理的数字,例如Take(1000)
。如果达到此阈值,您始终可以警告用户缩小其搜索范围
主要的问题可能是对标题
、机器名
和严重性
进行过滤时,每个调用都会调用AsEnumerable()
,这会具体化查询,因此您在内存中而不是在SQL中对这3个过滤器进行评估,可能会有大量记录。所有最新版本的EF都能够将形式为Where(c=>IEnumerable.Contains(c.Column))
的谓词转换为SQLWhere c.Column IN(X1,X2,X3)
i、 你应该
if (p_Criteria.Titles.Any())
{
internetQuery = internetQuery
.Where(c => p_Criteria.Titles.Contains(c.Title));
}
if (p_Criteria.MachineNamesAny())
{
internetQuery = internetQuery
.Where(c => p_Criteria.MachineNames.Contains(c.MachineName));
}
if (p_Criteria.Severities.Any())
{
internetQuery = internetQuery
.Where(c => p_Criteria.Severities.Contains(c.Severity));
}
internetQuery = internetQuery.Take(p_Criteria.TopValue);
if (!string.IsNullOrEmpty(p_Criteria.FreeText))
{
internetQuery = internetQuery
.Where(c => c.FormattedMessage.StartsWith(p_Criteria.FreeText));
}
if (p_Criteria.DateFrom != null && p_Criteria.DateFrom > DateTime.MinValue)
{
internetQuery = internetQuery.Where(c => c.Timestamp >= p_Criteria.DateFrom);
}
if (p_Criteria.DateTo != null && p_Criteria.DateTo > DateTime.MinValue)
{
internetQuery = internetQuery.Where(c => c.Timestamp < p_Criteria.DateTo);
}
internetQuery = internetQuery.AsEnumerable().Where(c => p_Criteria.Severities.Contains(c.Severity))
public static IQueryable<LogViewer.EF.InternetEF.Log> ExecuteInternetGetLogsQuery(FilterCriteria p_Criteria, ref GridView p_Datagrid)
{
IQueryable<LogViewer.EF.InternetEF.Log> internetQuery = null;
List<LogViewer.EF.InternetEF.Log> executedList = null;
using (InternetDBConnectionString context = new InternetDBConnectionString())
{
internetQuery = context.Logs;
if ((p_Criteria.DateTo != null && p_Criteria.DateFrom != null))
{
internetQuery = internetQuery.Where(c => c.Timestamp >= p_Criteria.DateFrom.Value && c.Timestamp < p_Criteria.DateTo.Value);
}
else if (p_Criteria.DateFrom != null && p_Criteria.DateFrom > DateTime.MinValue)
{
internetQuery = internetQuery.Where(c => c.Timestamp >= p_Criteria.DateFrom);
}
else if (p_Criteria.DateTo != null && p_Criteria.DateTo > DateTime.MinValue)
{
internetQuery = internetQuery.Where(c => c.Timestamp < p_Criteria.DateTo);
}
if (!string.IsNullOrEmpty(p_Criteria.FreeText))
{
internetQuery = internetQuery.Where(c => c.FormattedMessage.Contains(p_Criteria.FreeText));
}
if (p_Criteria.Titles.Count > 0)
{
internetQuery = internetQuery.Where(BuildOrExpression<LogViewer.EF.InternetEF.Log, string>(p => p.Title, p_Criteria.Titles));
}
if (p_Criteria.MachineNames.Count > 0)
{
internetQuery = internetQuery.Where(BuildOrExpression<LogViewer.EF.InternetEF.Log, string>(p => p.MachineName, p_Criteria.MachineNames));
}
if (p_Criteria.Severities.Count > 0)
{
internetQuery = internetQuery.Where(BuildOrExpression<LogViewer.EF.InternetEF.Log, string>(p => p.Severity, p_Criteria.Severities));
}
internetQuery = internetQuery.Take(p_Criteria.TopValue);
executedList = internetQuery.ToList<LogViewer.EF.InternetEF.Log>();
executedList = executedList.OrderByDescending(c => c.LogID).ToList<LogViewer.EF.InternetEF.Log>(); ;
p_Datagrid.DataSource = executedList;
p_Datagrid.DataBind();
return internetQuery;
}
}
public static Expression<Func<TElement, bool>> BuildOrExpression<TElement, TValue>(
Expression<Func<TElement, TValue>> valueSelector,
IEnumerable<TValue> values )
{
if (null == valueSelector)
throw new ArgumentNullException("valueSelector");
if (null == values)
throw new ArgumentNullException("values");
ParameterExpression p = valueSelector.Parameters.Single();
if (!values.Any())
return e => false;
var equals = values.Select(value =>
(Expression)Expression.Equal(
valueSelector.Body,
Expression.Constant(
value,
typeof(TValue)
)
)
);
var body = equals.Aggregate<Expression>(
(accumulate, equal) => Expression.Or(accumulate, equal)
);
return Expression.Lambda<Func<TElement, bool>>(body, p);
}