簡體   English   中英

在復雜的 RavenDb 對象中延遲加載

[英]Lazy loading in complex RavenDb objects

看起來我們正在處理一個深思熟慮的對象設計問題,現在它正在表現為重大的性能/內存問題。

我們在 RavenDb 數據庫中存儲了數千個根聚合對象。 對於某些大客戶,這些對象變得太大而無法有效執行 Web 操作(打開頁面、保存數據等)。

結構如下: Account 對象是聚合根 在它下面,有許多較小的對象和集合,它們的大小都“很好”,除了一個稱為 Resources 的集合,它可以增長得非常大,並可能導致根對象大小為數兆字節。 這會導致 Account 及其內部數據的基本 CRUD 操作執行速度非常慢

Resource 集合中的對象本身並不大,但它們有自己的子對象,並且會向上拖動大小。 每個 Resource 對象都有 Metrics、Actions、Alerts、Scaling 和其他“重”集合

我們的代碼庫非常復雜,有數十萬行代碼; 數百甚至數千行代碼引用 Resource 集合並檢查其中的 Resource 對象,但對每個 Resource 對象的底層子集合的訪問似乎很少見,並且一次只完成一個資源

問題:我們如何加載 Account 對象、其所有雜項子對象和對象,以及僅第一級 Resource 對象,然后延遲加載Resources 的子子對象? (有 7 個可以延遲加載的特定集合)

我們有一個負責加載/保存數據的存儲庫

我們如何加載 Account 對象、其所有雜項子對象和對象,以及僅第一級 Resource 對象,然后延遲加載 Resources 的子子對象? (有 7 個可以延遲加載的特定集合)

使用 Raven 進行按需加載非常簡單。 要做到這一點,讓你的資源擁有你想要延遲加載的東西作為它們自己的文檔,然后在父級上擁有一組 ID。

前:

class Resource
{
   public List<Foo> Foos { get; set; }
   public List<Bar> Bars { get; set; }
   // ... etc
}

后:

class Resource
{
   // These are the things we need to lazy load.
   public List<string> FooIds { get; set; }
   public List<string> BarIds { get; set; }
}

至於您的 Foo 和 Bar 對象(Resource 的延遲加載的子對象),您需要將它們存儲為它們自己的文檔。

一旦你這樣做了,加載一個 Resource 就不會加載它的所有子對象,讓你在讀寫時獲得性能提升。

但是當你需要加載那些孩子的時候呢? 使用 .Include:

// Query for Resource and include the children in a single remote call.
var resourcesWithChildren = docSession
   .Query<Resource>()
   .Include(r => r.FooIds) // Include the related Foos
   .Include(r => r.BarIds) // Include the related Bars
   .Where(...)
   .ToList();


foreach (var resource in resourcesWithChildren)
{
    // Grab the children; they're already loaded, so this won't induce a remote call.
    var foos = docSession.Load<Foo>(resource.FooIds);
    var bars = docSession.Load<Bar>(resource.BarIds);
}

我們如何加載 Account 對象、其所有雜項子對象和對象,以及僅第一級 Resource 對象,然后延遲加載 Resources 的子子對象? (有 7 個可以延遲加載的特定集合)

好的,我的另一個答案是分解巨大物體的推薦方法; 只是讓它們成為自己的獨立對象。

但是,既然你說你不想做分解它們的工作,那么還有另一種方法可以做到這一點,那就是使用變壓器。 使用轉換器不會使 Raven 免於加載大的 Account 對象及其所有子對象,但由於轉換器是在服務器上執行的,它不會通過網絡將大對象發送到您的 Web 服務器。

public class AccountWithFirstLevelResourcesTransformer : AbstractTransformerCreationTask<Account>
{
    public AccountWithFirstLevelResourcesTransformer()
    {
        TransformResults = accs => from acc in accs
                                   select new Account
                                   {
                                       ...
                                       Resources = acc.Resources.Select(fullResource => new Resource
                                       {
                                            // Only the properties we want loaded here.
                                            Name = fullResource.Name,
                                            ...
                                       })
                                       ...
                                   };
    }
}

您將在啟動期間安裝此轉換器:

new AccountWithFirstLevelResourcesTransformer().Execute(RavenStore); // RavenStore is your IDocumentStore singleton.

然后您的 .Load 調用將如下所示:

// This account will have only the first level resources.
var account = dbSession.Load<AccountWithFirstLevelResourcesTransformer, Account>("accounts/1");

暫無
暫無

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

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