c# - 实体框架 - 数百万条记录的快速插入/更新
问题描述
我需要将 190 万条新记录插入 MySQL 数据库。要使用它,我使用的是 C# Entity Framework,但这个过程似乎非常慢。按照目前的速度,处理这些记录需要几天时间。
我做错了什么,如何加快速度?
在数据库中,我有 2 个表:哈希和类别。每个散列应该是唯一的并且可以有多个类别,每个散列只有 1 个类别处于活动状态。
我需要遵循的过程是首先检查哈希是否存在。如果是这样,那么我需要找到当前类别,将其停用并添加新类别。
问题是我的try{ }
陈述大约需要 150 毫秒,而这个块SaveChanges()
大约需要 15-30 秒。因此,以这种方式处理 190 万条记录将需要几天时间。
using (var reader = new StreamReader(File.OpenRead(filepath)))
using (MySQLContext db = new MySQLContext(options))
{
// Disable auto detect changes
db.ChangeTracker.AutoDetectChangesEnabled = false;
int loopCounter = 0;
string line;
// Load up the db tables in memory
var hashes = db.Hashes.Select(x => x).ToList();
var category = db.Categories.Select(a => a).ToList();
while ((line = reader.ReadLine()) != null)
{
var matches = Regex.Matches(line, "(?<MD5>[a-zA-Z0-9]+)(?<Category>[0-9])");
InputHashModel inputHash = new InputHashModel()
{
MD5 = matches[0].Groups["MD5"].Value,
Category = matches[0].Groups["Category"].Value
};
try
{
// Check if hash already exists
Hash hash = hashes.Where(h => h.MD5 == inputHash.MD5).FirstOrDefault();
// If hash doesn't exist - add it
if (hash == null)
hash = new Hash(inputHash.MD5);
else
{
// Check if category already exists
Category category = categories.Where(a => a.Active == true && a.HashId == hash.Id).FirstOrDefault();
// If it exists - deactivate it
if (category != null)
{
// If the same category already exists - proceed to next hash
if (category.Source == "ThisInput" && category.Category == inputHash.Category)
{
loopCounter++
continue;
}
category.Active = false;
category.DeactivatedTimestamp = DateTime.Now;
}
}
// Add new category
Category new_category = new Category() { Hash = hash, Source = "ThisInput", Category = inputHash.Category, Active = true);
db.Categories.Add(new_category);
// Save changes every 1000
if (loopCounter % 1000 == 0)
{
db.ChangeTracker.DetectChanges();
db.SaveChanges();
}
}
catch (Exception e)
{
Console.WriteLine("Exception: " + e);
}
loopCounter++;
}
db.ChangeTracker.AutoDetectChangesEnabled = true;
db.SaveChanges();
Console.WriteLine("Finished");
}
解决方案
这永远不会是最快的方法,但至少您需要避免在更改跟踪器中累积所有实体。每次 SaveChanges() 运行后的 EG
foreach (var e in db.ChangeTracker.Entries())
{
e.State = EntityState.Detached;
}
推荐阅读
- jquery - 使用 Travery css/html 和 jquery 创建的计数器编辑
- r - 在 tidyverse 中使用 `anti_join` 函数
- php - Laravel 7:获取列是日期时间的两个日期之间的行
- node.js - 如何从我的 azure cosmos db 中查询数据?
- java - 如何更新 Swing Oval 的颜色
- php - 在终端更新 PHP
- java - CreateFromRessource ChipDrawable :此组件上的样式要求您的应用主题为 Theme.MaterialComponents
- python - Tweepy 不会响应用户名,而只会响应使用的字符串。任何线索我做错了什么?
- php - 如何在下拉列表中显示特定类别 ID 的子类别。在 laravel 内
- amazon-web-services - AWS cli/boto3- 是否可以知道角色或策略是否对资源具有权限?