草庐IT

MongoDB 复合索引优化键和范围条件更新

coder 2023-11-04 原文

已阅读this doc,它指出索引可以优化更新操作。然后,我将索引添加到我的集合中以优化我正在使用的更新操作。

集合中的记录具有作为_id 的对象和时间戳:

{_id: {userId: "sample"}, firstTimestamp: 123, otherField: "abc"}

我想做的是使用下面的查询操作更新:

db.userFirstTimestamp.update(
{_id: {userId: "sample"}, firstTimestamp: {$gt: 100}},
{_id: {userId: "sample"}, firstTimestamp: 100, otherField2: "efg"})

我想根据'firstTimestamp'存储'first document',旧文档和新文档的字段可以不同,因此它不能是$set查询,它应该改写文档。对于下面的示例,“otherField”不应该存在,它应该是“otherField2”。

基于我对 MongoDB 文档和 this article 的理解, 我按照下面创建了索引

db.sample.createIndex({_id:1, timestamp:1})

然后我尝试使用具有以下规范的 MongoDB 3.0.4 在一个独立的实验节点上对查询进行基准测试:

  • MongoDB 3.0.4
  • 机器是空的,没有其他操作,只有mongo
  • 内存 ~30GB
  • 磁盘已剥离 RAID 0
  • 馆藏有6000万条记录
  • 平均对象大小 1001 字节
  • 索引大小 5.34 gig

当我检查日志时,许多更新查询需要超过 100 毫秒,而当我执行 mongotop 时,查询的顶部是写入查询,大约需要 1000 毫秒。它有点慢,因为执行一个查询需要很长时间。

当我执行 mongostat 时,吞吐量仅为每秒 400-500 个查询

然后我尝试使用查找查询进行查询解释(因为更新不支持解释)

  • 当我不使用投影时,它使用默认索引 {_id:1}。
  • 当我仅对 _id 和时间戳使用投影时,它使用的是 {_id:1, timestamp:1} 索引。

我的问题是:

  1. 我创建的索引是否有助于更新查询?
  2. 如果没有帮助,那么索引应该如何设置?
  3. 还有其他方法可以优化此更新查询吗?

最佳答案

  1. 有点。但不是最优的。

  2. 确实应该是这样,所以在 _id 键中对象的“元素”上建立索引:

    db.sample.createIndex({ "_id.userId": 1, "timestamp": 1 })
    
  3. 使用 $set运算符(operator)并停止覆盖您的文档:

    db.sample.update(
        { 
            "_id.userId": "sample", 
            "firstTimestamp": { "$gt": 100 }
        },
        {
            "$set": { "otherfield": "cfg"  }
        }
    )
    

但实际上您的数据“应该”如下所示:

{
    "_id": "sample", 
    "firstTimestamp": 200,
    "otherfield2": "sam"
}

然后像这样更新:

    db.sample.update(
        { 
            "_id.userId": "sample", 
            "firstTimestamp": { "$gt": 100 }
        },
        {
            "$set": { 
                "fistTimetamp": 100,
                "otherfield2": "efg"
            }
        }
    )

或者,如果您坚持认为“_id”和“firstTimestamp”以外的字段会发生很大变化,那么最好这样做:

{
    "_id": "sample", 
    "firstTimestamp": 200,
    "data": {
        "otherfield2": "sam"
    }
}

如果您只是想替换数据,请执行以下操作:

    db.sample.update(
        { 
            "_id.userId": "sample", 
            "firstTimestamp": { "$gt": 100 }
        },
        {
            "$set": { 
                "fistTimetamp": 100,
                "data": {
                   "overwritingField": "efg"
                }
            }
        }
    )

如果您愿意,可以将“数据”替换为整个对象,或者只更新单个键:

    db.sample.update(
        { 
            "_id.userId": "sample", 
            "firstTimestamp": { "$gt": 100 }
        },
        {
            "$set": { 
                "fistTimetamp": 100,
                "data.newfield": "efg"
            }
        }
    )

在所有情况下,尝试使用运算符而不是替换整个对象,因为它通常会导致更多流量和服务器负载。

但总的来说,这里有意义的是“userId”部分“应该”是索引中最能缩小结果范围的部分。所以它肯定在时间戳之前,其中应该有更多可能的值。

复合主键很好,但请确保您实际使用它们。单一值没有任何意义,只能分配给 _id。如果您可以像这里一样只查询它们键的一个字段,那么您可能不需要将复合对象作为主键。

您在更新中的 _id 表明您正在获得与 _id 完全匹配的信息,因此它不是具有其他键的复合字段。在这种情况下,它应该只是 _id 本身的一个值。

“范围”也可以,但再次考虑到您正在尝试匹配单个文档(好吧,您没有在任何地方提到“多个”),所以再次询问为什么需要它,然后再去寻找一个确切的匹配或至少“至少”一个上限。

$set 将“仅”更新您指定的字段。我认为您在输入问题时犯了一个错误,因为“更新”部分的语法无效。但无论如何都要使用更新运算符,因为它们通过发送单个字段或仅发送您打算更新的字段来发送较少的流量。

关于MongoDB 复合索引优化键和范围条件更新,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/31691322/

有关MongoDB 复合索引优化键和范围条件更新的更多相关文章

  1. ruby-on-rails - 如何验证 update_all 是否实际在 Rails 中更新 - 2

    给定这段代码defcreate@upgrades=User.update_all(["role=?","upgraded"],:id=>params[:upgrade])redirect_toadmin_upgrades_path,:notice=>"Successfullyupgradeduser."end我如何在该操作中实际验证它们是否已保存或未重定向到适当的页面和消息? 最佳答案 在Rails3中,update_all不返回任何有意义的信息,除了已更新的记录数(这可能取决于您的DBMS是否返回该信息)。http://ar.ru

  2. ruby-on-rails - 使用 rails 4 设计而不更新用户 - 2

    我将应用程序升级到Rails4,一切正常。我可以登录并转到我的编辑页面。也更新了观点。使用标准View时,用户会更新。但是当我添加例如字段:name时,它​​不会在表单中更新。使用devise3.1.1和gem'protected_attributes'我需要在设备或数据库上运行某种更新命令吗?我也搜索过这个地方,找到了许多不同的解决方案,但没有一个会更新我的用户字段。我没有添加任何自定义字段。 最佳答案 如果您想允许额外的参数,您可以在ApplicationController中使用beforefilter,因为Rails4将参数

  3. ruby - 触发器 ruby​​ 中 3 点范围运算符和 2 点范围运算符的区别 - 2

    请帮助我理解范围运算符...和..之间的区别,作为Ruby中使用的“触发器”。这是PragmaticProgrammersguidetoRuby中的一个示例:a=(11..20).collect{|i|(i%4==0)..(i%3==0)?i:nil}返回:[nil,12,nil,nil,nil,16,17,18,nil,20]还有:a=(11..20).collect{|i|(i%4==0)...(i%3==0)?i:nil}返回:[nil,12,13,14,15,16,17,18,nil,20] 最佳答案 触发器(又名f/f)是

  4. ruby - 如何根据特征实现 FactoryGirl 的条件行为 - 2

    我有一个用户工厂。我希望默认情况下确认用户。但是鉴于unconfirmed特征,我不希望它们被确认。虽然我有一个基于实现细节而不是抽象的工作实现,但我想知道如何正确地做到这一点。factory:userdoafter(:create)do|user,evaluator|#unwantedimplementationdetailshereunlessFactoryGirl.factories[:user].defined_traits.map(&:name).include?(:unconfirmed)user.confirm!endendtrait:unconfirmeddoenden

  5. ruby-on-rails - 相关表上的范围为 "WHERE ... LIKE" - 2

    我正在尝试从Postgresql表(table1)中获取数据,该表由另一个相关表(property)的字段(table2)过滤。在纯SQL中,我会这样编写查询:SELECT*FROMtable1JOINtable2USING(table2_id)WHEREtable2.propertyLIKE'query%'这工作正常:scope:my_scope,->(query){includes(:table2).where("table2.property":query)}但我真正需要的是使用LIKE运算符进行过滤,而不是严格相等。然而,这是行不通的:scope:my_scope,->(que

  6. ruby - 在 Ruby 中有条件地定义函数 - 2

    我有一些代码在几个不同的位置之一运行:作为具有调试输出的命令行工具,作为不接受任何输出的更大程序的一部分,以及在Rails环境中。有时我需要根据代码的位置对代码进行细微的更改,我意识到以下样式似乎可行:print"Testingnestedfunctionsdefined\n"CLI=trueifCLIdeftest_printprint"CommandLineVersion\n"endelsedeftest_printprint"ReleaseVersion\n"endendtest_print()这导致:TestingnestedfunctionsdefinedCommandLin

  7. ruby - 当使用::指定模块时,为什么 Ruby 不在更高范围内查找类? - 2

    我刚刚被困在这个问题上一段时间了。以这个基地为例:moduleTopclassTestendmoduleFooendend稍后,我可以通过这样做在Foo中定义扩展Test的类:moduleTopmoduleFooclassSomeTest但是,如果我尝试通过使用::指定模块来最小化缩进:moduleTop::FooclassFailure这失败了:NameError:uninitializedconstantTop::Foo::Test这是一个错误,还是仅仅是Ruby解析变量名的方式的逻辑结果? 最佳答案 Isthisabug,or

  8. ruby - 定义方法参数的条件 - 2

    我有一个只接受一个参数的方法:defmy_method(number)end如果使用number调用方法,我该如何引发错误??通常,我如何定义方法参数的条件?比如我想在调用的时候报错:my_method(1) 最佳答案 您可以添加guard在函数的开头,如果参数无效则引发异常。例如:defmy_method(number)failArgumentError,"Inputshouldbegreaterthanorequalto2"ifnumbereputse.messageend#=>Inputshouldbegreaterthano

  9. Ruby 从大范围中获取第 n 个项目 - 2

    假设我有这个范围:("aaaaa".."zzzzz")如何在不事先/每次生成整个项目的情况下从范围中获取第N个项目? 最佳答案 一种快速简便的方法:("aaaaa".."zzzzz").first(42).last#==>"aaabp"如果出于某种原因你不得不一遍又一遍地这样做,或者如果你需要避免为前N个元素构建中间数组,你可以这样写:moduleEnumerabledefskip(n)returnto_enum:skip,nunlessblock_given?each_with_indexdo|item,index|yieldit

  10. sql - 查询忽略时间戳日期的时间范围 - 2

    我正在尝试查询我的Rails数据库(Postgres)中的购买表,我想查询时间范围。例如,我想知道在所有日期的下午2点到3点之间进行了多少次购买。此表中有一个created_at列,但我不知道如何在不搜索特定日期的情况下完成此操作。我试过:Purchases.where("created_atBETWEEN?and?",Time.now-1.hour,Time.now)但这最终只会搜索今天与那些时间的日期。 最佳答案 您需要使用PostgreSQL'sdate_part/extractfunction从created_at中提取小时

随机推荐