MongoDB在大型集合上的写入性能较差,并且有50.000.000个文档

Gui*_*mer 22 mongodb

我有一个MongoDB,它存储了204.639.403项的产品数据,这些数据已经由项目的国家分散到同一个MongoDB进程中同一物理机器上运行的四个逻辑数据库中.

这是一个列表,其中包含每个逻辑数据库的文档数:

  • CoUk:56.719.977
  • 德:61.216.165
  • Fr:52.280.460
  • 它:34.422.801

我的问题是数据库写入性能越来越差,特别是对四个数据库中最大的数据库(De)的写入变得非常糟糕,根据iotopmongod进程使用99%的IO时间,写入少于3MB,读取数据为1.5MB每秒.这导致长锁定数据库,100%+锁定通常根据mongostat- 即使所有进程写入和读取到其他国家/地区数据库已被停止.当前从站达到最大为6的LOAD,副本集主机同时具有2-3的负载,因此也导致复制滞后.

每个数据库都有相同的数据和索引结构,我使用最大的数据库(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)
    ]
}
Run Code Online (Sandbox Code Playgroud)

典型的查询形式简单,例如ProductId或EAN,仅按类别进行细化,并按类别进行排序或按类别排序或按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
}
Run Code Online (Sandbox Code Playgroud)

值得一提的是,索引大小几乎是存储大小的一半.

每个国家/地区数据库每天必须处理3-5百万次更新/插入,我的目标是在夜间不到五个小时内执行写入操作.

目前它是一个具有两个服务器的副本集,每个服务器具有32GB RAM和具有2TB HDD的RAID1.已经进行了诸如死锁调度程序和noatime之类的简单优化.

我制定了一些优化策略:

  • 减少数量索引:
    • 默认的_id可以使用ProductId而不是默认的MongoId,这样可以为每个nixes大小的每个数据库节省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作为分片键似乎不适合所有查询类型,并且类别的分片也很复杂.单个项目可以列在多个主要和子类别中.... 我的担忧可能是错的,我从未在生产环境中使用它.

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

小智 12

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

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