如何用EF7和SQLite讀取一千個對象

c# entity-framework-core sqlite win-universal-app

我正在使用EF7從SQLite DB中讀取40,000個非常小的對象,它需要18秒,這對我的UWP應用來說太長了。當發生這種情況時,單個核心上的CPU使用率達到100%,但磁盤讀取速度大約為1%。

var dataPoints =  _db.DataPoints.AsNoTracking().ToArray();

如果沒有AsNoTracking(),所用的時間會更長。 DataPoint是一個小型POCO,具有一些原始屬性。我加載的數據總量是4.5兆字節。

    public class DataPointDto
    {
        [Key]
        public ulong Id { get; set; }

        [Required]
        public DateTimeOffset TimeStamp { get; set; }

        [Required]
        public bool trueTime { get; set; }

        [Required]
        public double Value { get; set; }
   }

問題:是否有更好的方法來加載這麼多對象,還是我堅持這種性能水平?

有趣的事實: x86需要11秒,x64需要18秒。“優化代碼”會縮短一秒鐘。使用Async將執行時間推遲到30秒。

一般承認的答案

大多數答案遵循加載較少數據的常識,但在某些情況下,例如在這裡, 絕對積極地必須加載很多實體。那我們怎麼做呢?

性能不佳的原因

這個操作是否不可避免地需要這麼長時間?好吧,不是。我們只從磁盤加載一兆字節的數據,性能不佳的原因是數據被分成40,000個小實體。數據庫可以處理,但實體框架似乎很難設置所有這些實體,更改跟踪等。如果我們不打算修改數據,我們可以做很多事情。

我嘗試了三件事

基元

只加載一個屬性,然後獲得基元列表。

List<double> dataPoints =  _db.DataPoints.Select(dp => dp.Value).ToList();

這繞過了通常由實體框架執行的所有實體創建。此查詢花費了0.4秒,而原始查詢則為18秒。我們正在談論45(!)倍的改進。

匿名類型

當然,大多數情況下我們需要的不僅僅是一個基元數組我們可以在LINQ查詢中創建新對象。實體框架不會創建它通常會實現的實體,並且操作運行得更快。為方便起見,我們可以使用匿名對象。

var query = db.DataPoints.Select(dp => new {Guid ID = dp.sensorID, DateTimeOffset Timestamp = dp.TimeStamp, double Value = dp.Value});

此操作需要1.2秒,而正常檢索相同數量的數據需要18秒。

元組

我發現在我的情況下,使用元組而不是匿名類型可以稍微提高性能,以下查詢執行速度大約快30%:

var query = db.DataPoints.Select(dp => Tuple.Create(dp.sensorID, dp.TimeStamp, dp.Value));

其他方法

  1. 您不能在LinQ查詢中使用結構,因此這不是一個選項
  2. 在許多情況下,您可以將多個記錄組合在一起,以減少與檢索許多單個記錄相關的開銷。通過檢索較少的較大記錄,您可以提高性能。例如,在我的用例中,我每隔5分鐘,每天24小時都會進行一些測量。目前我正在單獨存儲它們,這很愚蠢。沒有人會查詢不到一天的價值。我計劃在進行更改時更新此帖子,並了解性能如何變化。
  3. 有些人建議使用面向對象的DB或微型ORM。我從來沒用過,所以我不能發表評論。

熱門答案

您可以使用其他技術加載所有項目。

你可以創建自己的邏輯來加載部分數據,同時用戶滾動ListView(我想你正在使用它)。

幸運的是,UWP是一種簡單的方法來完成這項技術。增量加載請參閱文檔和示例

https://msdn.microsoft.com/library/windows/apps/Hh701916



Related

許可下: CC-BY-SA with attribution
不隸屬於 Stack Overflow
這個KB合法嗎? 是的,了解原因
許可下: CC-BY-SA with attribution
不隸屬於 Stack Overflow
這個KB合法嗎? 是的,了解原因