【问题标题】:How do I optimize RavenDB queries for retrieving all documents?如何优化 RavenDB 查询以检索所有文档?
【发布时间】:2013-01-25 00:29:07
【问题描述】:

我正在尝试学习如何使用 RavenDB,为此我创建了一个基本示例。 看来初始化存储和查询需要花费大量时间!

static void Main( string[] args )
{
    const bool createNewEntities = true;

    var sw = new Stopwatch();
    using( var store = new EmbeddableDocumentStore {DataDirectory = "~\\Data"} )
    {
        sw.Start();
        store.Initialize();
        sw.Stop();
        Console.WriteLine( "Initialized in {0} ms.", sw.ElapsedMilliseconds );

        if (createNewEntities)
        {
            sw.Reset();
            sw.Start();
            using( var session = store.OpenSession() )
            {
                sw.Stop();
                Console.WriteLine();
                Console.WriteLine( "Opened session in {0} ms.", sw.ElapsedMilliseconds );

                for( var i = 0; i < 10; i++ )
                {
                    var entity = new EntityA( "Entity A " + DateTime.Now.ToLongTimeString() );

                    sw.Reset();
                    sw.Start();
                    session.Store( entity );
                    sw.Stop();

                    if (i < 3)
                        Console.WriteLine( "Stored '{0}' in {1} ms.", entity.Name, sw.ElapsedMilliseconds );
                }

                sw.Reset();
                sw.Start();
                session.SaveChanges();
                sw.Stop();
                Console.WriteLine( "Saved changes in {0} ms.", sw.ElapsedMilliseconds );
            }
        }


        sw.Reset();
        sw.Start();
        using( var session = store.OpenSession() )
        {
            sw.Stop();
            Console.WriteLine();
            Console.WriteLine( "Opened EntityA session in {0} ms.", sw.ElapsedMilliseconds );

            sw.Reset();
            sw.Start();
            var entities = session.Query<EntityA>().ToArray();
            sw.Stop();
            Console.WriteLine("Queried for all {0} EntityA in {1} ms.", entities.Length, sw.ElapsedMilliseconds);
        }


        sw.Reset();
        sw.Start();
        using( var session = store.OpenSession() )
        {
            sw.Stop();
            Console.WriteLine();
            Console.WriteLine( "Opened EntityA session (again) in {0} ms.", sw.ElapsedMilliseconds );

            sw.Reset();
            sw.Start();
            var entities2 = session.Query<EntityA>().ToArray();
            sw.Stop();
            Console.WriteLine( "Queried (again) for all {0} EntityA in {1} ms.", entities2.Length, sw.ElapsedMilliseconds );
        }
    }


    Console.WriteLine();
    Console.WriteLine();
    Console.WriteLine( "Press ENTER to exit..." );
    Console.ReadLine();
}

这会产生以下输出:

在 6132 毫秒内初始化。 在 3 毫秒内打开会话。 在 129 毫秒内存储了“实体 A 08:50:14”。 在 0 毫秒内存储了“实体 A 08:50:15”。 在 0 毫秒内存储了“实体 A 08:50:15”。 在 29 毫秒内保存更改。 在 0 毫秒内打开 EntityA 会话。 在 463 毫秒内查询所有 10 个 EntityA。 在 0 毫秒内(再次)打开 EntityA 会话。 在 1 毫秒内(再次)查询所有 10 个 EntityA。

从这个粗略的例子中,我可以看出:

  • 初始化商店需要大量时间!!
  • 存储第一个实体(十个)需要相当长的时间。
  • 第一次查询所有实体需要很长时间,但第二次完全没有时间。

如何正确查询数据库中的所有特定类型 (EntityA) 的文档? 当然,RavenDB 不可能每个查询都需要索引吗?尤其是对于没有任何条件的查询?

(注意:我打算使用嵌入在桌面应用程序中的 DB,其中列出所有文档用于显示 DB 的内容。)

【问题讨论】:

  • 这类“为什么”问题的答案通常很难找到,也很难应用(因此,“没有建设性”)。与其问为什么会这样,您可能想编辑以询问您需要在代码中执行哪些类型的操作以优化性能。
  • 很公平。有了这样一个基本示例,我的目标是提供一个解释性答案,例如“您使用错了,因为...”而不是简单的是/否答案,例如“是的,使用索引”,它不能解释“为什么”。但是你的观点是有道理的。我会尝试改写我的问题!

标签: c# ravendb document-database


【解决方案1】:

以下是三个延迟的原因:

初始化延迟
初始化文档存储确实是最昂贵的操作之一。由于您正在运行 RavenDB 的嵌入式模式,它不仅要建立与数据库的连接,而且实际上还必须启动数据库运行。在我的机器(一台 2.3Ghz i5 笔记本电脑)上,初始化需要 2516 毫秒。

如果您运行的是完整的 RavenDB 服务器(非嵌入式) - 大部分延迟将是在启动服务器本身时。初始化客户端会明显更快。

这是合理的行为,考虑到IDocumentStore(无论是嵌入的还是普通的)都应该作为单例来保存。在您的应用程序中应该只有一个这样的实例,并且应该在启动时创建并在关闭时释放。

首次存储延迟
因为您没有提供自己的Id,Raven 会使用其HiLo generation algorithm 为您自动生成一个。这涉及从数据库中分配一个可分配的 id 块,这确实需要很少的时间。后续调用会更快,因为它们不必在块用完之前访问数据库。

如果您提供自己的 Id 属性并用有效标识符(例如 entities/1entities/2 等)填充它 - 那么它会更快,因为您将跳过密钥生成。

查询延迟
当您未指定静态索引时,第一次调用 .Query&lt;T&gt;() 将尝试创建与查询表达式匹配的动态索引。即使在获取“所有”实体时也是如此,因为它仍然必须使用Raven-Entity-Name 元数据按实体类型进行过滤。 RavenDB 中的Collections 是一个虚拟的东西,由元数据决定。这些文档实际上是一起存在的 - 因此除了通过元数据查询和过滤之外,没有其他方法可以将所有项目放入“集合”中。

您看到的部分延迟是正在构建的动态索引。然后对要索引的项目有延迟。请注意,如果您添加了更多项目(例如几百个),您仍然会得到大致相同的延迟,但您不会取回所有项目。该索引会因为刚刚创建而过时,并且 Raven 只会返回其中的一小部分。在像您这样的测试中,您可能希望显式等待non-stale results。在实际应用程序中,您可能希望改为预定义 static index。实际上,您可以通过反对静态索引来加快查询速度。延迟将移至索引创建时间而不是查询时间。

如果你想完全避免使用索引,还有另一种方法:

session.Advanced.LoadStartingWith<EntityA>("EntityAs/");

此方法不使用元数据进行过滤 - 它使用键名本身。它直接与文档存储区相冲突,无需查询 - 因此速度要快得多。您需要paginate 才能获得大量结果 - 但无论如何您都对查询有同样的担忧。但是使用这种方法,默认页面大小要小得多(25) - 所以你肯定会很快遇到这个问题。

我希望这能解答您的疑虑。如果您有其他人,请在 cmets 中告诉我。

【讨论】:

  • 太棒了!这些答案正是我所希望的,甚至更多!他们应该把你放在 RavenDB 文档中! (:谢谢!!
猜你喜欢
  • 1970-01-01
  • 2020-06-30
  • 1970-01-01
  • 2022-09-27
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-03-04
  • 1970-01-01
相关资源
最近更新 更多