[英]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.