(DbContext) SaveChanges() 如何用于大量记录? - 寻求和找到最佳性能的解决方案
How can (DbContext) SaveChanges() be used for large number of records? - the solution for the best performance is to be sought and found
对于大量记录可以运行SaveChanges(),但是性能不能接受。
这是一个例子
两个 table:
[联系人](身份证、姓名、邮政编码、城市、状态)
[zip_city](身份证、邮编、城市)
需要更新 table contact
中所有列 zip 的状态,城市在 table zip_city
中可用
有效,当记录数小于10000条时性能acceptable
但是对于 +10,000 条记录,Visual Studio(调试器)抱怨它花费的时间太长:CLR 无法在 60 秒内从 COM 上下文 0xb67898 转换到 COM 上下文 0xb67728.... 在构建查询 "qry"
var ctx = new DbContext();
var qry = ctx.contact.Join(ctx.zip_city, c => new { c.zip, c.city }, z => new { z.zip, z.city }, (c, z) => new { c, z })
.Select(x => new dtoContact { id = x.c.id }).ToList();
foreach (var con in ctx.contact)
{
if (qry.Any(x => x.Id == con.id))
{
con.status = "P/O";
}
else
{
con.status = "???";
}
}
ctx.SaveChanges();
使用以下代码,它会通过 运行 ctx.SaveChanges();
引发相同的消息
var ctx = new DbContext();
var zc = ctx.zip_city.ToList();
foreach (var con in ctx.contact)
{
if (zc.Any(x => x.zip == con.zip && x.city == con.city))
{
con.status = "P/O";
}
else
{
con.status = "???";
}
}
ctx.SaveChanges();
参考:数据传输对象
public class dtoContact
{
public int id { get; set; }
public string name { get; set; }
public string zip { get; set; }
public string city { get; set; }
public string status { get; set; }
}
信息:Visual Studio(调试器)消息!
google 翻译自 VS15 德文版
托管调试助手“ContextSwitchDeadlock”在 "C: \ Projects \ Sweepstakes_EF6 \ TrafficSightAct \ bin \ Debug \ Sweepstakes.exe" 中遇到问题。
附加信息:CLR 在 60 秒内无法从 COM 上下文 0xb67898 转换到 COM 上下文 0xb67728。拥有目标上下文/单元的线程要么等待而不移动消息,要么处理一个非常持久的操作而不移动 Windows 消息。这种情况通常会降低性能,甚至可能导致应用程序停止响应或增加内存使用量。
在这种情况下,我想推荐使用 SqlCommand。
例如:
var ctx = new DbContext();
string sql = "update contact c inner join zip_city z " +
"on c.plz = z.plz and c.ort = z.ort set c.status = 'P/O'; ";
sql = sql + "update contact set status = '???' where status <> 'P/O'; ";
ctx.Database.ExecuteSqlCommand(sql);
我想,您的 27,000 条记录可能只需要几秒钟。
请尝试。
感谢大家提出宝贵意见!
为了让上面的问题评论保持一致,我在这里创建一个答案。
我在评论中测试了在 table 联系人中提供的 27,000 条记录和 table zip_city 中的 47,000 条记录的建议,但没有任何数据库索引更改(添加 Panagiotis Kanavos 建议的索引)。
以下为性能测试结果
原码1,耗时113秒.
var ctx = new DbContext();
var qry = ctx.contact.Join(ctx.zip_city, c => new { c.zip, c.city }, z => new { z.zip, z.city }, (c, z) => new { c, z })
.Select(x => new dtoContact { id = x.c.id }).ToList();
foreach (var con in ctx.contact)
{
if (qry.Any(x => x.Id == con.id))
{
con.status = "P/O";
}
else
{
con.status = "???";
}
}
ctx.SaveChanges();
基于 DevilSuichiro,“bulk savechanges”在 MySQL 的 DbContext 中不可用,因此这里仅对 100 个实体的单个批次调用 SaveChanges()。
需要 107 秒.
var ctx = new DbContext();
var qry = ctx.contact.Join(ctx.zip_city, c => new { c.zip, c.city }, z => new { z.zip, z.city }, (c, z) => new { c, z })
.Select(x => new dtoContact { id = x.c.id }).ToList();
int count = 0;
foreach (var con in ctx.contact)
{
if (qry.Any(x => x.Id == con.id))
{
con.status = "P/O";
}
else
{
con.status = "???";
}
++count;
if (count % 100 == 0)
{
ctx.SaveChanges();
}
}
ctx.SaveChanges();
在Rand Random的基础上,使用HashSet来提高性能。
仍然需要 100 多秒。
var ctx = new DbContext();
var qry = ctx.contact.Join(ctx.zip_city, c => new { c.zip, c.city }, z => new { z.zip, z.city }, (c, z) => new { c, z }).Select(id = x.c.id);
var ids = new HashSet<int>(qry);
foreach (var con in ctx.contact)
{
if (ids.Contains(con.id))
{
con.status = "P/O";
}
else
{
con.status = "???";
}
}
ctx.SaveChanges();
原码2,耗时67秒.
var ctx = new DbContext();
var zc = ctx.zip_city.ToList();
foreach (var con in ctx.contact)
{
if (zc.Any(x => x.zip == con.zip && x.city == con.city))
{
con.status = "P/O";
}
else
{
con.status = "???";
}
}
ctx.SaveChanges();
基于 Panagiotis Kanavos,为此数据更新使用任何 ORM(如此处 DbContext SaveChanges() )确实不是合适的解决方案。
我测试了来自 alex, nvoigt 的代码,不到 2 秒。
var ctx = new DbContext();
string sql = "update contact set status = '???'; " +
"update contact c inner join zip_city z on c.plz = z.plz and c.ort = z.ort set c.status = 'P/O'; ";
ctx.Database.ExecuteSqlCommand(sql);
对于大量记录可以运行SaveChanges(),但是性能不能接受。
这是一个例子 两个 table:
[联系人](身份证、姓名、邮政编码、城市、状态)
[zip_city](身份证、邮编、城市)
需要更新 table contact
中所有列 zip 的状态,城市在 table zip_city
有效,当记录数小于10000条时性能acceptable
但是对于 +10,000 条记录,Visual Studio(调试器)抱怨它花费的时间太长:CLR 无法在 60 秒内从 COM 上下文 0xb67898 转换到 COM 上下文 0xb67728.... 在构建查询 "qry"
var ctx = new DbContext();
var qry = ctx.contact.Join(ctx.zip_city, c => new { c.zip, c.city }, z => new { z.zip, z.city }, (c, z) => new { c, z })
.Select(x => new dtoContact { id = x.c.id }).ToList();
foreach (var con in ctx.contact)
{
if (qry.Any(x => x.Id == con.id))
{
con.status = "P/O";
}
else
{
con.status = "???";
}
}
ctx.SaveChanges();
使用以下代码,它会通过 运行 ctx.SaveChanges();
引发相同的消息var ctx = new DbContext();
var zc = ctx.zip_city.ToList();
foreach (var con in ctx.contact)
{
if (zc.Any(x => x.zip == con.zip && x.city == con.city))
{
con.status = "P/O";
}
else
{
con.status = "???";
}
}
ctx.SaveChanges();
参考:数据传输对象
public class dtoContact
{
public int id { get; set; }
public string name { get; set; }
public string zip { get; set; }
public string city { get; set; }
public string status { get; set; }
}
信息:Visual Studio(调试器)消息! google 翻译自 VS15 德文版
托管调试助手“ContextSwitchDeadlock”在 "C: \ Projects \ Sweepstakes_EF6 \ TrafficSightAct \ bin \ Debug \ Sweepstakes.exe" 中遇到问题。 附加信息:CLR 在 60 秒内无法从 COM 上下文 0xb67898 转换到 COM 上下文 0xb67728。拥有目标上下文/单元的线程要么等待而不移动消息,要么处理一个非常持久的操作而不移动 Windows 消息。这种情况通常会降低性能,甚至可能导致应用程序停止响应或增加内存使用量。
在这种情况下,我想推荐使用 SqlCommand。
例如:
var ctx = new DbContext();
string sql = "update contact c inner join zip_city z " +
"on c.plz = z.plz and c.ort = z.ort set c.status = 'P/O'; ";
sql = sql + "update contact set status = '???' where status <> 'P/O'; ";
ctx.Database.ExecuteSqlCommand(sql);
我想,您的 27,000 条记录可能只需要几秒钟。
请尝试。
感谢大家提出宝贵意见!
为了让上面的问题评论保持一致,我在这里创建一个答案。
我在评论中测试了在 table 联系人中提供的 27,000 条记录和 table zip_city 中的 47,000 条记录的建议,但没有任何数据库索引更改(添加 Panagiotis Kanavos 建议的索引)。
以下为性能测试结果
原码1,耗时113秒.
var ctx = new DbContext();
var qry = ctx.contact.Join(ctx.zip_city, c => new { c.zip, c.city }, z => new { z.zip, z.city }, (c, z) => new { c, z })
.Select(x => new dtoContact { id = x.c.id }).ToList();
foreach (var con in ctx.contact)
{
if (qry.Any(x => x.Id == con.id))
{
con.status = "P/O";
}
else
{
con.status = "???";
}
}
ctx.SaveChanges();
基于 DevilSuichiro,“bulk savechanges”在 MySQL 的 DbContext 中不可用,因此这里仅对 100 个实体的单个批次调用 SaveChanges()。 需要 107 秒.
var ctx = new DbContext();
var qry = ctx.contact.Join(ctx.zip_city, c => new { c.zip, c.city }, z => new { z.zip, z.city }, (c, z) => new { c, z })
.Select(x => new dtoContact { id = x.c.id }).ToList();
int count = 0;
foreach (var con in ctx.contact)
{
if (qry.Any(x => x.Id == con.id))
{
con.status = "P/O";
}
else
{
con.status = "???";
}
++count;
if (count % 100 == 0)
{
ctx.SaveChanges();
}
}
ctx.SaveChanges();
在Rand Random的基础上,使用HashSet来提高性能。 仍然需要 100 多秒。
var ctx = new DbContext();
var qry = ctx.contact.Join(ctx.zip_city, c => new { c.zip, c.city }, z => new { z.zip, z.city }, (c, z) => new { c, z }).Select(id = x.c.id);
var ids = new HashSet<int>(qry);
foreach (var con in ctx.contact)
{
if (ids.Contains(con.id))
{
con.status = "P/O";
}
else
{
con.status = "???";
}
}
ctx.SaveChanges();
原码2,耗时67秒.
var ctx = new DbContext();
var zc = ctx.zip_city.ToList();
foreach (var con in ctx.contact)
{
if (zc.Any(x => x.zip == con.zip && x.city == con.city))
{
con.status = "P/O";
}
else
{
con.status = "???";
}
}
ctx.SaveChanges();
基于 Panagiotis Kanavos,为此数据更新使用任何 ORM(如此处 DbContext SaveChanges() )确实不是合适的解决方案。
我测试了来自 alex, nvoigt 的代码,不到 2 秒。
var ctx = new DbContext();
string sql = "update contact set status = '???'; " +
"update contact c inner join zip_city z on c.plz = z.plz and c.ort = z.ort set c.status = 'P/O'; ";
ctx.Database.ExecuteSqlCommand(sql);