首页 > 解决方案 > 80k 列表的糟糕 linq 性能

问题描述

我有一个来自 Web 服务的约 80,000 个项目列表,我需要根据以下内容计算出要同步到本地数据库的项目:

迭代需要 30 到 60 秒,特别是toInsert在线上。我不认为 80k 有很多记录(TickerV2结构大约是 10 个小字段,大部分是 int)。

我一定是在做一些可怕的事情,请问有什么想法可以提高性能吗?

public class TickerV2
{
    public string Ticker { get; set; } // Ticker is the key by which we operate
    public string Name { get; set; }
    public Market Market { get; set; }
    public Locale Locale { get; set; }
    public TickerType Type { get; set; }
    public bool Active { get; set; }
    public string PrimaryExch { get; set; }
    public DateTime Updated { get; set; }
    public CurrencyCodes Currency { get; set; }
    // note the Market, Locale, CurrencyCode are all enum but not indexed
}

async Task SaveTickersToDatabaseAsync(IEnumerable<TickerV2> web)
{
    using var connection = new SqlConnection(this.dbConnectionString);

    await connection.OpenAsync();
    var db = connection.Query<TickerV2>("SELECT * FROM Tickers").ToList();
    var dbHashset = db.Select(x => x.Ticker).ToImmutableHashSet();
    var webHashset = web.Select(x => x.Ticker).ToImmutableHashSet();
    var toDeleteTickers = dbHashset.Except(webHashset).ToList();
    var toInsertTickers = webHashset.Except(dbHashset).ToList();

    var toInsert = web.Where(x => toInsertTickers.Contains(x.Ticker)).ToList();

    var toUpdate = db
                    .Join(web, dbData => dbData.Ticker, web => web.Ticker, (db, web) => new { Db = db, Web = web })
                    .Where(joined => joined.Web.Updated > joined.Db.Updated)
                    .Select(x => x.Web)
                    .ToList();

}

使用字典更新

使用下面的方法我得到了巨大的速度提升......我猜以前我们在每次迭代中都在搜索Contains(这是顺序的??)Where- 这个陈述是否正确?

代码变为:

var toInsert = new List<TickerV2>();
var webDictionary = web.ToImmutableDictionary(x => x.Ticker);
toInsert.AddRange(from tickerKey in toInsertTickers
                  select webDictionary[tickerKey]);

但不确定在问题和其他运营商的背景下,这是否是最好的方法?

标签: c#performancelinq

解决方案


查询可以优化如下。在内存中加载如此多的条目可能会导致内存泄漏。尝试在数据库中应用过滤器,虽然不是数据库专家不能建议查询。我相信不需要转换为哈希集,因为它只是为了比较而导致开销。

        IEnumerable<TickerV2> web = new TickerV2[0];
        IEnumerable<TickerV2> db = new TickerV2[0];
        
        var entriesMissingFromDb =  web.Except(db, new TickerV2Comparer());

        var toInsert = db.Join(entriesMissingFromDb, _db => _db.Ticker, _web => _web.Ticker, (_db, _web) => _web)
            .ToList();

比较器如下

    public class TickerV2Comparer : IEqualityComparer<TickerV2>
    {
        public bool Equals(TickerV2 x, TickerV2 y)
        {
            if (ReferenceEquals(x, y))
                return true;

            if (x == null || y == null)
                return false;

            return x.Ticker.Equals(y.Ticker);
        }

        public int GetHashCode(TickerV2 obj)
        {
            return obj.Ticker.GetHashCode();
        }
    }

推荐阅读