草庐IT

MongoDB 在包含 50.000.000 个文档的大型集合上写入性能不佳

coder 2023-05-04 原文

我有一个 MongoDB,用于存储 的产品数据204.639.403 项目,这些数据已经按项目所在的国家/地区吐出到四逻辑在同一 MongoDB 进程中的同一台物理机器上运行的数据库。

以下是每个逻辑数据库的文档数列表:

  • CoUk: 56.719.977
  • 德: 61.216.165
  • 神父: 52.280.460
  • 它: 34.422.801

  • 我的问题是数据库写入性能越来越差,特别是写入四个数据库中最大的(De)变得非常糟糕,据iotop mongod 进程使用 99% 每秒少于 3MB 的写入和 1.5MB 的读取的 IO 时间。这会导致长时间锁定数据库, 100%+ 根据mongostat锁正常- 即使所有写入和读取其他国家数据库的进程都已停止。当前slave的LOAD达到6,副本集master同时有2-3的load,因此也会导致复制延迟。

    每个数据库都有相同的数据和索引结构,我使用最大的数据库 (De) 仅作为进一步的示例。

    这是从数据库中随机抽取的项目,例如,优化结构以通过单次读取收集所有重要数据:
    {
        "_id" : ObjectId("533b675dba0e381ecf4daa86"),
        "ProductId" : "XGW1-E002F-DW",
        "Title" : "Sample item",
        "OfferNew" : {
            "Count" : 7,
            "LowestPrice" : 2631,
            "OfferCondition" : "NEW"
        },
        "Country" : "de",
        "ImageUrl" : "http://….jpg",
        "OfferHistoryNew" : [ 
            … 
            {
                "Date" : ISODate("2014-06-01T23:22:10.940+02:00"),
                "Value" : {
                    "Count" : 10,
                    "LowestPrice" : 2171,
                    "OfferCondition" : "NEW"
                }
            }
        ],
        "Processed" : ISODate("2014-06-09T23:22:10.940+02:00"),
        "Eans" : [ 
            "9781241461959"
        ],
        "OfferUsed" : {
            "Count" : 1,
            "LowestPrice" : 5660,
            "OfferCondition" : "USED"
        },
        "Categories" : [ 
            NumberLong(186606), 
            NumberLong(541686), 
            NumberLong(288100), 
            NumberLong(143), 
            NumberLong(15777241)
        ]
    }
    

    典型的查询是简单的,例如按 ProductId 或 EAN 仅按类别细化并按其 A 等级排序或按类别和 A 等级范围(例如 1 到 10.000)细化并按 B 等级排序…… .

    这是来自最大数据库的统计数据:
    {
        "ns" : "De.Item",
        "count" : 61216165,
        "size" : 43915150656,
        "avgObjSize" : 717,
        "storageSize" : 45795192544,
        "numExtents" : 42,
        "nindexes" : 6,
        "lastExtentSize" : 2146426864,
        "paddingFactor" : 1,
        "systemFlags" : 0,
        "userFlags" : 1,
        "totalIndexSize" : 41356824320,
        "indexSizes" : {
            "_id_" : 2544027808,
            "RankA_1" : 1718096464,
            "Categories_1_RankA_1_RankB_-1" : 16383534832,
            "Eans_1" : 2846073776,
            "Categories_1_RankA_-1" : 15115290064,
            "ProductId_1" : 2749801376
        },
        "ok" : 1
    }
    

    值得一提的是,索引大小为将近一半的存储大小 .

    各国DB 每天必须处理 3-5 百万次更新/插入 ,我的目标是在夜间不到五个小时内执行写操作。

    目前它是一个带有两台服务器的副本集,每台服务器都有 32GB 内存和一个带有 2TB HDD 的 RAID1。死锁调度程序和 noatime 等简单优化已经完成。

    我已经制定了一些优化策略:
  • 减少数字索引:
  • 默认 _id 可以使用 ProductId 而不是默认的 MongoId,这将为每个总 nixes 大小为每个 DB 节省 6-7%。
  • 尝试删除 Categories_1_RankA_-1 索引,也许 BrowseNodes_1_RankA_1_RankB_-1 索引也可以处理查询。当不使用完整索引时,排序仍然表现良好吗?另一种方法是将匹配 Categories_1_RankA_1_RankB_-1 的索引存储在另一个引用主集合的集合中。
  • 通过使用较小的键来减少原始数据量,而不是“类别”、“Eans”、“OfferHistoryNew”……我可以使用“a”、“b”、“c”……这应该很容易,因为我使用了 http://mongojack.org/但我现在不知道这会有多值得。
  • 用 RAID0 替换 RAID1,可以通过关闭从属设备、重新安装并将其读取到副本集来轻松测试......。
  • 测试更强大的硬件 SSD 和更多内存,以更快地处理读取和写入。
  • 使用 MongoDB 的着色功能:
  • 我读到每个分片都必须保存整个数据库索引?
  • 我担心查询结构可能不适合共享环境。使用产品 ID 作为分片键似乎并不适合所有查询类型,而且按类别进行分片也很复杂。单个项目可以列在多个主要和子类别中……。我的担心可能是错误的,我从未在生产环境中使用过它。

  • 但是应该还有其他优化策略,我想听的也没有想到!
    哪种优化策略听起来最有希望,或者这里需要多种优化的混合?

    最佳答案

    由于创纪录的增长,您很可能会遇到问题,请参阅 http://docs.mongodb.org/manual/core/write-performance/#document-growth .

    Mongo 更喜欢固定(或至少有界)大小的记录。将记录大小增加到超出预先分配的存储空间将导致文档被移动到磁盘上的另一个位置,从而使每次写入的 I/O 成倍增加。如果您的文档大小相对相同,请考虑在插入时为您的平均文档预先分配“足够”的空间。否则考虑将快速增长的嵌套数组拆分为一个单独的集合,从而用插入替换更新。还要检查您的碎片并考虑不时压缩您的数据库,以便您每个块拥有更高密度的文档,这将减少硬页面错误。

    关于MongoDB 在包含 50.000.000 个文档的大型集合上写入性能不佳,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/24868171/

    有关MongoDB 在包含 50.000.000 个文档的大型集合上写入性能不佳的更多相关文章

    1. ruby - 检查 "command"的输出应该包含 NilClass 的意外崩溃 - 2

      为了将Cucumber用于命令行脚本,我按照提供的说明安装了arubagem。它在我的Gemfile中,我可以验证是否安装了正确的版本并且我已经包含了require'aruba/cucumber'在'features/env.rb'中为了确保它能正常工作,我写了以下场景:@announceScenario:Testingcucumber/arubaGivenablankslateThentheoutputfrom"ls-la"shouldcontain"drw"假设事情应该失败。它确实失败了,但失败的原因是错误的:@announceScenario:Testingcucumber/ar

    2. Ruby 写入和读取对象到文件 - 2

      好的,所以我的目标是轻松地将一些数据保存到磁盘以备后用。您如何简单地写入然后读取一个对象?所以如果我有一个简单的类classCattr_accessor:a,:bdefinitialize(a,b)@a,@b=a,bendend所以如果我从中非常快地制作一个objobj=C.new("foo","bar")#justgaveitsomerandomvalues然后我可以把它变成一个kindaidstring=obj.to_s#whichreturns""我终于可以将此字符串打印到文件或其他内容中。我的问题是,我该如何再次将这个id变回一个对象?我知道我可以自己挑选信息并制作一个接受该信

    3. ruby - 检查字符串是否包含散列中的任何键并返回它包含的键的值 - 2

      我有一个包含多个键的散列和一个字符串,该字符串不包含散列中的任何键或包含一个键。h={"k1"=>"v1","k2"=>"v2","k3"=>"v3"}s="thisisanexamplestringthatmightoccurwithakeysomewhereinthestringk1(withspecialcharacterslike(^&*$#@!^&&*))"检查s是否包含h中的任何键的最佳方法是什么,如果包含,则返回它包含的键的值?例如,对于上面的h和s的例子,输出应该是v1。编辑:只有字符串是用户定义的。哈希将始终相同。 最佳答案

    4. Matlab imread()读到了什么 (浅显 当复习文档了) - 2

      matlab打开matlab,用最简单的imread方法读取一个图像clcclearimg_h=imread('hua.jpg');返回一个数组(矩阵),往往是a*b*cunit8类型解释一下这个三维数组的意思,行数、数和层数,unit8:指数据类型,无符号八位整形,可理解为0~2^8的数三个层数分别代表RGB三个通道图像rgb最常用的是24-位实现方法,即RGB每个通道有256色阶(2^8)。基于这样的24-位RGB模型的色彩空间可以表现256×256×256≈1670万色当imshow传入了一个二维数组,它将以灰度方式绘制;可以把图像拆分为rgb三层,可以以灰度的方式观察它figure(1

    5. ruby - Ruby 是否使用 $stdout 来写入 puts 和 return 的输出? - 2

      我想知道Ruby用来在命令行打印这些东西的输出流:irb(main):001:0>a="test"=>"test"irb(main):002:0>putsatest=>nilirb(main):003:0>a=>"test"$stdout是否用于irb(main):002:0>和irb(main):003:0>?而且,在这两次调用之间,$stdout的值是否有任何变化?另外,有人能告诉我打印/写入这些内容的Ruby源代码吗? 最佳答案 是的。而且很容易向自己测试/证明。在命令行试试这个:ruby-e'puts"foo"'>test.

    6. ruby-on-rails - 使用包含多个关联和单独的条件 - 2

      我的Gallery模型中有以下查询:media_items.includes(:photo,:video).rank(:position_in_gallery)我的图库模型有_许多媒体项,每个都有一个照片或视频关联。到目前为止,一切正常。它返回所有media_items包括它们的photo或video关联,由media_item的position_in_gallery属性排序。但是我现在需要将此查询返回的照片限制为仅具有is_processing属性的照片,即nil。是否可以进行相同的查询,但条件是返回的照片等同于:.where(photo:'photo.is_processingIS

    7. ruby - 我怎样才能只写一次 "Text"并同时检查 path_info 是否包含 'A' ? - 2

      -if!request.path_info.include?'A'%{:id=>'A'}"Text"-else"Text"“文本”写了两次。我怎样才能只写一次并同时检查path_info是否包含“A”? 最佳答案 有两种方法可以做到这一点。使用部分,或使用content_forblock:如果“文本”较长,或者是一个重要的子树,您可以将其提取到一个部分。这会使您的代码变干一点。在给出的示例中,这似乎有点矫枉过正。在这种情况下更好的方法是使用content_forblock,如下所示:-if!request.path_info.inc

    8. Ruby,使用包含 TK GUI 的 ocra 部署一个 exe - 2

      Ocra无法处理需要“tk”的应用程序require'tk'puts'nope'用奥克拉http://github.com/larsch/ocra不起作用(如链接中的一个问题所述)问题:https://github.com/larsch/ocra/issues/29(Ocra是1.9的"new"rubyscript2exe,本质上它用于将rb脚本部署为可执行文件)唯一的问题似乎是缺少tcl的DLL文件我不认为这是一个问题据我所知,问题是缺少tk的DLL文件如果它们是已知的,则可以在执行ocra时将它们包括在内有没有办法知道tk工作所需的DLL依赖项? 最佳答

    9. ruby - 允许主机名包含下划线的 URI.parse 的替代方法 - 2

      我正在使用DMOZ的listofurltopics,其中包含一些具有包含下划线的主机名的url。例如:608609TheOuterHeaven610InformationandimagegalleryofMcFarlane'sactionfiguresforTrigun,Akira,TenchiMuyoandotherJapaneseSci-Fianimations.611Top/Arts/Animation/Anime/Collectibles/Models_and_Figures/Action_Figures612虽然此url可以在网络浏览器中使用(或者至少在我的浏览器中可以使用:

    10. Ruby:写入 stdin 并从 stdout 读取? - 2

      我正在编写一个ruby​​程序,它应该执行另一个程序,通过stdin向它传递值,从它的stdout读取响应,然后打印响应。这是我目前所拥有的。#!/usr/bin/envrubyrequire'open3'stdin,stdout,stderr=Open3.popen3('./MyProgram')stdin.puts"helloworld!"output=stdout.readerrors=stderr.readstdin.closestdout.closestderr.closeputs"Output:"puts"-------"putsoutputputs"\nErrors:"p

    随机推荐