是否有可能提高Mongoexport的速度?

时间:2018-05-03 01:24:22

标签: mongodb performance

我有一个130M行的MongoDB 3.6.2.0集合。它有几个简单的字段和2个带有嵌套JSON文档的字段。数据以压缩格式(zlib)存储。

我需要尽快将其中一个嵌入字段导出为JSON格式。然而,mongoexport正在永远。运行12个小时后,它只处理了5.5%的数据,这对我来说太慢了。

CPU不忙。 Mongoexport似乎是单线程的。

我正在使用的导出命令:

mongoexport -c places --fields API \
    --uri mongodb://user:pass@hostIP:hostPort/maps?authSource=admin \
    -o D:\APIRecords.json

它实际上是getMore命令,它在引擎盖下非常慢:

2018-05-02T17:59:35.605-0700 I COMMAND  [conn289] command maps.places command: getMore { getMore: 14338659261, collection: "places", $db: "maps" } originatingCommand: { find: "places", filter: {}, sort: {}, projection: { _id: 1, API: 1 }, skip: 0, snapshot: true, $readPreference: { mode: "secondaryPreferred" }, $db: "maps" } planSummary: COLLSCAN cursorid:14338659261 keysExamined:0 docsExamined:5369 numYields:1337 nreturned:5369 reslen:16773797 locks:{ Global: { acquireCount: { r: 2676 } }, Database: { acquireCount: { r: 1338 } }, Collection: { acquireCount: { r: 1338 } } } protocol:op_query 22796ms

我尝试在这样的单独进程中使用--SKIP--LIMIT选项运行多个命令

mongoexport -c places --SKIP 10000000 --LIMIT 10000000 --fields API \
    --uri mongodb://user:pass@hostIP:hostPort/maps?authSource=admin \
    -o D:\APIRecords.json
mongoexport -c places --SKIP 20000000 --LIMIT 10000000 --fields API \
    --uri mongodb://user:pass@hostIP:hostPort/maps?authSource=admin \
    -o D:\APIRecords.json

等。但是我无法完成等待直到第一个非零SKIP的命令开始!

我也尝试使用--forceTableScan选项,这没有任何区别。

我在地方表上没有索引。

我的存储配置:

journal.enabled: false
wiredTiger.collectionConfig.blockCompressor: zlib

收集统计数据:

'ns': 'maps.places',
'size': 2360965435671,
'count': 130084054,
'avgObjSize': 18149,
'storageSize': 585095348224.0

我的服务器规格:

Windows Server 2012 R2 x64
10Gb RAM 4TB HDD 6 cores Xeon 2.2Ghz

我已经进行了测试,并且使用SSD,它具有与HDD相同的可读读取吞吐量。

我的问题:

为什么阅读速度这么慢?还有其他人遇到过同样的问题吗?你能给我一些关于如何加速数据转储的提示吗?

更新

我将数据库移动到快速的NVME SSD驱动器,我想现在我可以更明确地说明我对MongoDB读取性能的担忧。

为什么这个命令试图找到一块没有特定字段的文件:

2018-05-05T07:20:46.215+0000 I COMMAND  [conn704] command maps.places command: find { find: "places", filter: { HTML: { $exists: false }, API.url: { $exists: true } }, skip: 9990, limit: 1600, lsid: { id: UUID("ddb8b02c-6481-45b9-9f84-cbafa586dbbf") }, $readPreference: { mode: "secondaryPreferred" }, $db: "maps" } planSummary: COLLSCAN cursorid:15881327065 keysExamined:0 docsExamined:482851 numYields:10857 nreturned:101 reslen:322532 locks:{ Global: { acquireCount: { r: 21716 } }, Database: { acquireCount: { r: 10858 } }, Collection: { acquireCount: { r: 10858 } } } protocol:op_query 177040ms

只能在快速闪存驱动器上产生50Mb /秒的读取压力?这显然是单线程随机(分散)读取的表现。我刚刚证明该驱动器可以轻松实现1Gb /秒的读/写吞吐量。

就Mongo内部而言,按顺序读取BSON文件并提高20倍扫描速度是不是更明智? (并且,由于我的块是zlib压缩的,并且服务器有16个内核,最好在一个或多个辅助线程中解码获取的块?)而不是在文档之后迭代BSON文档。

我也可以确认,即使我没有指定任何查询过滤器,并且显然想要迭代ENTIRE集合,也不会发生BSON文件的快速顺序读取。

4 个答案:

答案 0 :(得分:3)

有许多因素限制了出口业绩。

  • 与可用内存相比,数据大小相对较大:~2 TB与~5 GB WiredTiger缓存(如果设置为默认值)。那是:
    • 整个WiredTiger缓存只能包含最好 ~0.22%的集合,实际上它很可能比这少得多,因为缓存将包含来自其他集合和索引的数据
    • 这意味着WiredTiger需要非常频繁地从磁盘中获取,同时逐出缓存的当前内容。如果正在积极使用副本集,这将意味着驱逐"脏"来自缓存的数据并将它们保存到磁盘,这需要时间。
    • 请注意,WiredTiger缓存中的文档不会被压缩。
  • 该集合包含大型文档,您只需要其中的一部分。这意味着处理文档需要额外的时间。
  • 使用zlib压缩集合,这意味着必须使用额外的时间来解压缩文档。
  • readPreference为secondaryPreferred,意味着它将尝试从辅助节点读取。如果正在主动写入副本集,则辅助节点上的oplog应用操作将阻止读取器。这将进一步延迟。

一个可能的改进是,如果这是您经常执行的操作,则在相关字段上创建索引并使用covered query导出它可以提高性能,因为索引将小于完整文档。< / p>

修改:在这种情况下并行运行mongoexport可能会有所帮助:

除了提供的其他信息之外,我还进行了一项似乎在某种程度上缓解这个问题的测试。

似乎并行运行mongoexport,其中每个mongoexport处理集合的子集可能会加快导出。

为此,请将_id命名空间与您计划运行的mongoexport进程数相对应。

例如,如果我有200,000个文档,从_id:0开始到_id:199,999并使用2个mongoexport进程:

mongoexport -q '{"_id":{"$gte":0, "$lt":100000}}' -d test -c test > out1.json &
mongoexport -q '{"_id":{"$gte":100000, "$lt":200000}}' -d test -c test > out2.json &

在上面的示例中,两个mongoexport进程分别处理集合的一半。

使用1个流程,2个流程,4个流程和8个流程测试此工作流程,我将按以下时间进行测试:

使用1个过程:

real    0m32.720s
user    0m33.900s
sys 0m0.540s

2个过程:

real    0m16.528s
user    0m17.068s
sys 0m0.300s

4个过程:

real    0m8.441s
user    0m8.644s
sys 0m0.140s

8个流程:

real    0m5.069s
user    0m4.520s
sys 0m0.364s

根据可用资源,并行运行8个mongoexport进程似乎可以将进程加速到6倍。这是在具有8个核心的机器中测试的。

注意:halfer的回答与想法类似,尽管这个答案基本上试图了解并行调用mongoexport是否有任何好处。

答案 1 :(得分:3)

您可以尝试使用pandas和joblib库将部分导出到JSON文件。您可以参考这个要点来处理MongoDB中的数据。

   extension AudioViewController : UICollectionViewDataSource,UICollectionViewDelegate,UICollectionViewDelegateFlowLayout{
        func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
            return 10
        }

        // make a cell for each cell index path
        func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {

            // get a reference to our storyboard cell
            let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "AudioCollectionViewCell", for: indexPath) as! AudioCollectionViewCell

 cell.backgeoundImage.image = UIImage(named :"play.png")

            return cell
        }

 *

    @objc(collectionView:layout:insetForSectionAtIndex:)  func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, insetForSectionAt section: Int) -> UIEdgeInsets{
            return UIEdgeInsetsMake(0, 100, 0, 0)
        }

*

from pandas import DataFrame from joblib import Parallel,delayed def process(idx,cursor): file_name = "fileName"+str(idx)+".json" df = DataFrame(list(cursor)) df.to_json(file_name, orient='records') #make a list of cursors.. you can read the parallel_scan api of pymongo cursors = mongo_collection.parallel_scan(no_of_parts_of_collection) Parallel(n_jobs=4)(delayed(process)(idx,cursor) for idx,cursor in enumerate(cursors) 参数应生成等于指定数量的进程。每个过程应包含一个核心。我使用4,因为您的服务器有6个核心可用。 n_jobs api接受一个数字,并将集合划分为与提供的数字相等的部分。您可以尝试使用更高的数字将集合分解为均匀划分的游标。

我尝试过类似的方法,但我的parallel_scan()函数的签名和定义却不同。我能够在20分钟内处理2.5M记录。你可以阅读我的this answer来了解我到底想要达到的目的。

答案 2 :(得分:1)

我不使用Mongo,但可以使用一个常见技巧:创建一个简单的应用程序,有效地顺序查询所有数据,过滤它并以您想要的格式保存。

如果你需要以复杂的格式保存并且没有库可以使用它(我真的很怀疑),读取所有内容,过滤,将其放回临时集合,导出该集合可能仍然有效完全放弃临时收集。

答案 3 :(得分:0)

mongoexport是一个客户端库,它使用与mongodb本身的公共API和套接字连接。

因此它无法访问磁盘上的文档BSON

这看起来像你提到的吗?

https://docs.mongodb.com/manual/core/backups/#back-up-by-copying-underlying-data-files

mongodump也可以作为你的选择