如何优化RavenDB查询以检索所有文档?

时间:2013-02-09 08:03:31

标签: c# ravendb document-database

我正在尝试学习如何使用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();
}

这会产生以下输出:

Initialized in 6132 ms.

Opened session in 3 ms.
Stored 'Entity A 08:50:14' in 129 ms.
Stored 'Entity A 08:50:15' in 0 ms.
Stored 'Entity A 08:50:15' in 0 ms.
Saved changes in 29 ms.

Opened EntityA session in 0 ms.
Queried for all 10 EntityA in 463 ms.

Opened EntityA session (again) in 0 ms.
Queried (again) for all 10 EntityA in 1 ms.

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

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

如何正确查询数据库中某种类型的所有文档(EntityA)? 当然,RavenDB不能要求每个查询都有一个索引吗?特别是没有任何标准的查询?

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

1 个答案:

答案 0 :(得分:5)

以下是三次延误的原因:

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

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

这是合理的行为,考虑到IDocumentStore(无论是嵌入的还是正常的)是要保持为单身。在您的应用程序中应该只有一个这样的实例,它应该在启动时创建并在关闭时处理。

首次延迟商店
由于您没有提供自己的Id,因此Raven会使用HiLo generation algorithm为您自动生成一个。{1}}。这涉及从数据库中分配一个可分配的ID块,这需要花费很少的时间。后续调用会更快,因为在块用完之前它们不必命中数据库。

如果您提供自己的Id媒体资源,并使用entities/1entities/2等有效标识符填充它,那么它会更快,因为您会跳过密钥生成。

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

您看到的部分延迟是正在构建的动态索引。然后,对项目进行索引会有延迟。请注意,如果您添加了更多项目(比如几百个),您仍然可以获得相同的延迟,但是您不会收回所有项目。该索引自刚刚创建以来就是陈旧的,而Raven只返回少量索引。在像您这样的测试中,您可能希望明确等待non-stale results。在实际应用程序中,您可能希望预先定义static index。实际上,您可以通过违反静态索引来加快查询速度。延迟将被移动到索引创建时而不是查询时间。

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

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

此方法不使用元数据进行过滤 - 它使用密钥名称本身。它没有查询就直接反对文档存储 - 所以它更快。您需要paginate才能获得大量结果 - 但无论如何您对查询都有同样的担忧。但是使用这种方法,默认的页面大小要小得多(25) - 所以你肯定会很快遇到这种情况。

我希望这能解决您的疑虑。如果你有其他人,请在评论中告诉我。