我使用ADO .Net Entity Model
来查询MySQL数据库。我对它的实现和使用感到非常高兴。我决定看看如果我查询了100万条记录并且它有严重的性能问题会发生什么,我不明白为什么。
系统挂起一段时间然后我得到
我的代码如下::
try
{
// works very fast
var data = from employees in dataContext.employee_table
.Include("employee_type")
.Include("employee_status")
orderby employees.EMPLOYEE_ID descending
select employees;
// This hangs the system and causes some deadlock exception
IList<employee_table> result = data.ToList<employee_table>();
return result;
}
catch (Exception ex)
{
throw new MyException("Error in fetching all employees", ex);
}
我的问题是为什么ToList()花了这么长时间?
另外,如何避免此异常以及查询百万条记录的理想方法是什么?
答案 0 :(得分:12)
查询一百万条记录的理想方法是使用IQueryable<T>
确保在您需要实际数据之前实际上没有对数据库执行查询。我非常怀疑你一次需要一百万条记录。
它是死锁的原因是你要求MySQL服务器从数据库中提取那些百万条记录然后按EMPLOYEE_ID
排序,然后让你的程序将它返回给你。所以我想象死锁是来自你的程序等待它完成,以及你的程序将其读入内存。 MySQL问题可能与超时问题有关。
var data
部分快速工作的原因是因为您实际上还没有做任何事情,您刚刚构建了查询。当您调用ToList()
时,将执行所有SQL和SQL读取。这就是所谓的延迟加载。
我建议尝试如下:
var data = from employees in dataContext.employee_table
.Include("employee_type")
.Include("employee_status")
orderby employees.EMPLOYEE_ID descending
select employees;
然后,如果您确实需要列表中的某些内容,请致电
data.Where(/* your filter expression */).ToList()
因此,如果您需要ID为10的员工。
var employee = data.Where(e => e.ID == 10).ToList();
或者,如果您需要姓氏以S开头的所有员工(我不知道您的表是否有姓氏列,只是一个示例)。
var employees = data.Where(e => e.LastName.StartsWith("s")).ToList();
或者,如果您想以100块的方式浏览所有员工
var employees = data.Skip(page * 100).Take(100).ToList();
如果您想进一步推迟数据库调用,则无法调用ToList()
并在需要时使用迭代器。所以,假设您想要将名称以A开头的人的所有工资加起来
var salaries = data.Where(s => s.LastName.StartsWith("A"))
foreach(var employee in salaries)
{
salaryTotal += employee.Salary;
}
这只会进行类似
的查询Select Salary From EmployeeTable Where ID = @ID
导致一个非常快速的查询,只在您需要时获取信息,而且只获取您需要的信息。
如果出于某种疯狂的原因,您想要实际查询数据库的所有百万条记录。忽略这会占用大量系统资源这一事实我建议以块的形式进行,你可能需要使用块大小来获得最佳性能。
一般的想法是进行较小的查询以避免数据库出现超时问题。
int ChunkSize = 100; //for example purposes
HashSet<Employee> Employees - new HashSet<Employee>;
//Assuming it's exactly 1 Million records
int RecordsToGet = 1000000;
for(record = 0; record <= RecordsToGet; record += ChunkSize)
{
dataContext.EmployeeTable.Skip(record).Take(ChunkSize).ForEach(e => HashSet.Add(e));
}
我选择使用HashSet<T>
,因为它们是为大型数据集而设计的,但我不知道哪些性能看起来像1,000,000个对象。