我正在处理我的应用程序中的100万条记录,这是从MySQL数据库中检索的。为此,我使用Linq获取记录并使用.Skip()和.Take()一次处理250条记录。对于每个检索到的记录,我需要创建0到4个项目,然后将其添加到数据库中。因此,必须创建的项目总平均数量约为200万。
IQueryable<Object> objectCollection = dataContext.Repository<Object>();
int amountToSkip = 0;
IList<Object> objects = objectCollection.Skip(amountToSkip).Take(250).ToList();
while (objects.Count != 0)
{
using (dataContext = new LinqToSqlContext(new DataContext()))
{
foreach (Object objectRecord in objects)
{
// Create 0 - 4 Random Items
for (int i = 0; i < Random.Next(0, 4); i++)
{
Item item = new Item();
item.Id = Guid.NewGuid();
item.Object = objectRecord.Id;
item.Created = DateTime.Now;
item.Changed = DateTime.Now;
dataContext.InsertOnSubmit(item);
}
}
dataContext.SubmitChanges();
}
amountToSkip += 250;
objects = objectCollection.Skip(amountToSkip).Take(250).ToList();
}
现在创建项目时出现问题。运行应用程序(,甚至不使用dataContext )时,内存会持续增加。这就像物品永远不会被处理掉。有没有人注意到我做错了什么?
提前致谢!
答案 0 :(得分:7)
好的,我刚刚和我的一位同事讨论过这种情况,我们已经找到了以下有效的解决方案!
int amountToSkip = 0;
var finished = false;
while (!finished)
{
using (var dataContext = new LinqToSqlContext(new DataContext()))
{
var objects = dataContext.Repository<Object>().Skip(amountToSkip).Take(250).ToList();
if (objects.Count == 0)
finished = true;
else
{
foreach (Object object in objects)
{
// Create 0 - 4 Random Items
for (int i = 0; i < Random.Next(0, 4); i++)
{
Item item = new Item();
item.Id = Guid.NewGuid();
item.Object = object.Id;
item.Created = DateTime.Now;
item.Changed = DateTime.Now;
dataContext.InsertOnSubmit(item);
}
}
dataContext.SubmitChanges();
}
// Cumulate amountToSkip with processAmount so we don't go over the same Items again
amountToSkip += processAmount;
}
}
通过这个实现,我们每次都处理Skip()和Take()缓存,因此不会泄漏内存!
答案 1 :(得分:5)
啊,好旧的InsertOnSubmit
内存泄漏。当我尝试使用LINQ to SQL从大型CVS文件加载数据时,我遇到过这种情况并且多次撞到墙上。问题是即使在调用SubmitChanges
后,DataContext
仍会继续跟踪使用InsertOnSubmit
添加的所有对象。在一定数量的对象之后,解决方案是SubmitChanges
,然后为下一批创建一个新的DataContext
。当旧的DataContext
被垃圾收集时,它所跟踪的所有插入对象(以及您不再需要的)也将被收集。
“但是等等!”你说,“创建和处理许多DataContext会产生巨大的开销!”。好吧,如果您创建单个数据库连接并将其传递给每个DataContext
构造函数,那就不是了。这样,始终保持与数据库的单个连接,DataContext
对象是一个轻量级对象,代表一个小工作单元,应该在完成后丢弃(在您的示例中,提交一定数量的记录)。
答案 2 :(得分:2)
我最好的猜测是导致内存泄漏的IQueryable。 也许没有针对Take / Skip方法的MySQL的适当实现,它在内存中进行分页?发生了奇怪的事情,但你的循环看起来很好。所有引用都应该超出范围并收集垃圾..
答案 3 :(得分:0)
好吧。
因此,在该循环结束时,您将尝试在列表中包含200万个项目,不是吗?在我看来,答案是微不足道的:存储更少的物品或获得更多的记忆。
- 编辑:
我可能读错了,我可能需要编译和测试它,但我现在不能这样做。我会把它留在这里,但我可能是错的,我没有仔细审查它以确定,但是答案可能证明是有用的,或者不是。 (通过downvote判断,我猜不是:P)
答案 4 :(得分:0)
您是否曾尝试在循环外声明项目,如下所示:
IQueryable<Object> objectCollection = dataContext.Repository<Object>();
int amountToSkip = 0;
IList<Object> objects = objectCollection.Skip(amountToSkip).Take(250).ToList();
Item item = null;
while (objects.Count != 0)
{
using (dataContext = new LinqToSqlContext(new DataContext()))
{
foreach (Object objectRecord in objects)
{
// Create 0 - 4 Random Items
for (int i = 0; i < Random.Next(0, 4); i++)
{
item = new Item();
item.Id = Guid.NewGuid();
item.Object = objectRecord.Id;
item.Created = DateTime.Now;
item.Changed = DateTime.Now;
dataContext.InsertOnSubmit(item);
}
}
dataContext.SubmitChanges();
}
amountToSkip += 250;
objects = objectCollection.Skip(amountToSkip).Take(250).ToList();
}