我有一个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文件的快速顺序读取。
答案 0 :(得分:3)
有许多因素限制了出口业绩。
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
也可以作为你的选择