如何使用 MongoDB 作为唯一/枚举存储

Posted

技术标签:

【中文标题】如何使用 MongoDB 作为唯一/枚举存储【英文标题】:How to use MongoDB as unique/enumeration store 【发布时间】:2012-07-06 09:33:43 【问题描述】:

这似乎是一个常见的用例......但不知何故我无法让它工作。

我正在尝试将 MongoDB 用作具有唯一项的枚举存储。我创建了一个带有 byte[] Id(唯一 ID)和时间戳(长,用于枚举)的集合。该存储非常大(TB)并且分布在不同的服务器之间。我目前能够从头开始重建商店,因为我仍处于测试阶段。

我想做的是两件事:

    为我插入的每个项目创建一个唯一的 ID。这基本上意味着如果我两次插入相同的 ID,MongoDB 将检测到这一点并给出错误。这种方法似乎效果很好。 通过其他进程不断枚举商店中的新项目。我采用的方法是向 InsertID 添加第二个索引,并在其上使用高精度时间戳以及服务器 ID 和计数器(只是为了使其唯一且升序)。

在最好的情况下,这意味着枚举器将跟踪每个服务器的索引游标。根据我从 mongodb 查询处理中学到的知识,我期望这种行为。但是,当我尝试执行代码(如下)时,似乎需要很长时间才能得到任何东西。

        long lastid = 0;
        while (true)
        
            DateTime first = DateTime.UtcNow;
            foreach (var item in collection.FindAllAs<ContentItem>().OrderBy((a)=>(a.InsertId)).Take(100))
            
                lastid = item.InsertId;
            
            Console.WriteLine("Took 0:0.00 for 100", (DateTime.UtcNow - first).TotalSeconds);
        

我读过游标,但不确定它们是否满足将新项目插入商店时的要求。

正如我所说,我不受任何表结构或类似的东西的约束......唯一重要的是我可以随着时间的推移获得新项目并且不会获得重复的项目。

-斯特凡。

【问题讨论】:

我不确定我是否有你的问题!但是,为什么不让 mongodb 生成 uniqueId。它已经做到了! 您需要确保一个字符串不会被枚举两次,或者以另一种方式说两个 id 不应该具有相同的值! 听起来您正在使用分片(多台计算机)。你这里的分片键是什么?你有多少台服务器?通过跨多个服务器的二级索引查询可能是您的问题,但我需要有关您的配置的更多详细信息。 @Ozair:我生成了一个唯一的 id,因为我用它来进行重复检测。 (1) ID 基本上是我插入的内容的 MD5 哈希值;如果我插入两次,它会发生碰撞并给出错误——这是正确的。 @GatesVP 是的,你是对的:我们目前对 14 个 MongoDB 实例使用分片(未来可能会增长)。分片键是 MD5 哈希。辅助键是 InsertId。换句话说,我确实希望枚举查询能够访问 14 台服务器。尽管如此,对于 100 个结果的查询来说,20 秒还是太长了。我期望的行为是查询到达所有服务器,获取约 10 个结果(您可以从统计数据中轻松推断出,因为每个服务器都持有或多或少相等数量的文档),合并它们并将它们发送回客户。 【参考方案1】:

不知怎的,我想通了……或多或少……

我手动创建了查询,结果如下:

db.documents.find( "InsertId" : "$gt" : NumberLong("2020374866209304106") ).limit(10).sort( "InsertId" : 1 );

我在问题中提出的 LINQ 查询不会生成此查询。在对代码进行了一些挖掘之后,我发现它应该是这个 LINQ 查询:

foreach (var item in collection.AsQueryable().Where((a)=>(a.InsertId > lastid)).OrderBy((a) => (a.InsertId)).Take(100))

AsQueryable() 似乎是执行 LINQ to MongoDB 查询重写的关键。

这给出了结果,但它们似乎仍然很慢(10 个结果需要 4 秒,100 个结果需要 30 秒)。但是,当我添加“解释()”时,我注意到查询执行中的“0 毫秒”。

我停止了批量插入和 tada 的过程,它可以工作,而且速度很快。换句话说:我遇到的问题是由于 MongoDB 的锁定行为,以及我解释 linq 实现的方式。由于前者是初始批量填充数据存储的结果,这意味着问题已解决。

关于解决方案的“负面”部分:我更喜欢涉及可序列化游标或类似内容的解决方案......这个“采取”解决方案必须一遍又一遍地迭代 b 树。如果有人对此有答案,请告诉我。

-斯特凡。

【讨论】:

1) 因为总是从 id 的开头排序(大概是索引的),所以除了 id 中的 100 个项目之外,我看不到你在哪里“迭代”b-tree和索引。不知道它会如何获得其中的 100 个...... 2) 您的问题提到了多个读者。使用您的做法,是什么阻止了两个不同的读者处理同一条记录? @CraigWilson 它没有;我只是为每个“进程”(例如索引等)枚举相同的集合。老实说,我想做并发的事情,但实际上我在 5 分钟前刚刚解决了这部分难题 :-) MongoDB 有一个很棒的东西,您可以连接到各个副本集并进行查询。如果您拥有与副本集数量一样多的流程实例,您可以简单地为每个流程实例枚举一个副本集。实际上,这就是我正在寻找的完整解决方案。顺便说一句:这种能力是 MongoDB 恕我直言的绝妙 USP。 @CraigWilson BTW 我真的不关心 100 或 500 或任何批量大小。我希望 MongoDB 只给我任何方便的批量大小——这可能是 B 树中的一个页面。然后光标就是页面ID。 '>=x take y' 只是告诉 MongoDB '给我一个永远持久的游标'的一种不方便的方式......如果 MongoDB 使用前一种方法实现带有链表的 B-tree(B+-tree)是相当效率低下......但不确定这是否是您的问题? 我想我不明白为什么你认为 '>=x take y' 不方便。这就是我们告诉人们实现分页的方式。它将在索引中寻找正确的记录并抓取下一个 100:非常有效。无论如何,我们确实支持可尾游标 (mongodb.org/display/DOCS/Tailable+Cursors)。它们仅在有上限的集合上受支持,这可能不适合您的需求。

以上是关于如何使用 MongoDB 作为唯一/枚举存储的主要内容,如果未能解决你的问题,请参考以下文章

如何使MongoDB的列成为SQL Server中发生的主键,避免重复记录?

如何在 MongoDB 2.4.x 中存储附加数据以使其符合 geoJSON 格式

如何使用 MongoDB 存储和搜索大型文档?

如何将嵌套枚举作为关联值传递但隐藏嵌套枚举的初始化?

如何使用 spring security 和 spring boot 对 Google 用户进行身份验证,将 mongoDB 作为存储库?

Mongo数据库如何使用电子邮件作为唯一键?