簡體   English   中英

改進 EFCore 查詢以實現快速操作

[英]Improve EFCore Query for fast operation

我讀了一個 sqlite 數據庫(數據庫大小約為 3 MB,所以信息不多,每個表大約 1 或 2 千行)並從中提取信息,然后將這些信息添加到新數據庫中。 整個操作大約需要 40 秒。

我怎樣才能減少這段時間並盡快完成手術? (任務,並行,異步,...)

我目前正在使用此代碼:

await Task.Run(async () =>
            {
                var pkgs = new ManifestTable();
                var mydb = new dbContext();
                await mydb.Database.EnsureDeletedAsync();
                await mydb.Database.EnsureCreatedAsync();
                using (var msixDB = new MSIXContext())
                {
                    foreach (var item in await msixDB.IdsMSIXTable.ToListAsync())
                    {
                        var rowId = item.rowid;
                        var manifests = await msixDB.Set<ManifestMSIXTable>().Where((e) => e.id == rowId).ToListAsync();

                        foreach (var manifest in manifests)
                        {
                            pkgs = new ManifestTable();
                            pkgs.PackageId = item.id;


                            var productMap = await msixDB.ProductCodesMapMSIXTable.FirstOrDefaultAsync((e) => e.manifest == manifest.rowid);
                            if (productMap != null)
                            {
                                var prdCode = await msixDB.ProductCodesMSIXTable.FirstOrDefaultAsync((e) => e.rowid == productMap.productcode);
                                if (prdCode != null)
                                {
                                    pkgs.ProductCode = prdCode.productcode;
                                }
                            }
                            var publisherMap = await msixDB.Set<PublishersMapMSIXTable>().FirstOrDefaultAsync((e) => e.manifest == manifest.rowid);

                            if (publisherMap != null)
                            {
                                var publisher = await msixDB.PublishersMSIXTable.FirstOrDefaultAsync((e) => e.rowid == publisherMap.norm_publisher);

                                if (publisher != null)
                                {
                                    pkgs.Publisher = publisher.norm_publisher;
                                }
                            }

                            var pathPart = manifest.pathpart;
                            var yml = await msixDB.PathPartsMSIXTable.FirstOrDefaultAsync((e) => e.rowid == pathPart);
                            if (yml != null)
                            {
                                pkgs.YamlName = yml.pathpart;
                            }

                            var version = await msixDB.VersionsMSIXTable.FirstOrDefaultAsync((e) => e.rowid == manifest.version);
                            if (version != null)
                            {
                                pkgs.Version = version.version;
                            }
                            await mydb.ManifestTable.AddAsync(pkgs);
                        }
                    }
                     await mydb.SaveChangesAsync();
                }

            });

將數據庫視為 object 存儲是有史以來最糟糕的主意。 您必須盡可能減少數據庫往返。 在您的情況下 - 只需一個請求。 如果您不知道哪個部分慢,也不要使用 Task.Run、Parallel 等。 在您的情況下 - 數據庫往返。

var mydb = new dbContext();
await mydb.Database.EnsureDeletedAsync();
await mydb.Database.EnsureCreatedAsync();

using (var msixDB = new MSIXContext())
{
    var query = 
        from item in msixDB.IdsMSIXTable
        from manifest in msixDB.Set<ManifestMSIXTable>().Where(e => e.id == item.rowId)
        from productMap in msixDB.ProductCodesMapMSIXTable.Where(e => e.manifest == manifest.rowid).Take(1).DefaultIfEmpty()
        from prdCode in msixDB.ProductCodesMSIXTable.Where(e => e.rowid == productMap.productcode).Take(1).DefaultIfEmpty();
        from publisherMap in msixDB.Set<PublishersMapMSIXTable>().Where(e => e.manifest == manifest.rowid).Take(1).DefaultIfEmpty()
        from publisher in msixDB.PublishersMSIXTable.Where(e => e.rowid == publisherMap.norm_publisher).Take(1).DefaultIfEmpty()
        from yml in msixDB.PathPartsMSIXTable.Where(e => e.rowid == manifest.pathpart).Take(1).DefaultIfEmpty()
        from version in msixDB.VersionsMSIXTable.Where(e => e.rowid == manifest.version).Take(1).DefaultIfEmpty()
        select new ManifestTable
        {
            PackageId = item.id,
            ProductCode = prdCode.productcode,
            Publisher = publisher.norm_publisher,
            YamlName = yml.pathpart,
            Version = version.version
        };

    mydb.ManifestTable.AddRange(await query.ToListAsync());
    await mydb.SaveChangesAsync();
}

您應該首先查看是否有任何算法改進,然后再嘗試並行執行等操作。

您有兩個嵌套循環,因此如果每個表有幾千行,則內部循環主體將以 10^6 的幅度運行,這並不可怕,但數量相當可觀。

然后在內部循環中運行一大堆FirstOrDefaultAsync語句。 如果這些沒有索引,則需要掃描所有行,這會很慢。 因此,首先要確保所有表都有適當的索引。 這樣做是為了確保在恆定時間內搜索特定項目。

您似乎也在使用相同的參數重復查找PublishersMapMSIXTable 避免不必要的重復操作應該是首先要解決的問題之一,因為這只是浪費周期。

如果整個操作在后台線程上運行,所有異步調用不太可能有太大幫助,它會節省一點 memory,但會導致線程之間出現一些反彈。 因此,如果重要的常規同步方法的性能可能會快一點。

和往常一樣,在性能方面,衡量 一個好的性能分析器應該告訴你大部分時間都花在了哪些地方,如果你沒有秒表,添加一些秒表很容易。 如果他們試圖猜測慢的部分是什么,即使是非常有經驗的程序員也可能完全錯誤。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM