首页 > 解决方案 > 实体框架 - 数百万条记录的快速插入/更新

问题描述

我需要将 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");
}

标签: c#.net-coreentity-framework-6

解决方案


这永远不会是最快的方法,但至少您需要避免在更改跟踪器中累积所有实体。每次 SaveChanges() 运行后的 EG

    foreach (var e in db.ChangeTracker.Entries())
    {
        e.State = EntityState.Detached;
    }

推荐阅读