MongoDB 创建大量集合测试问题

本文涉及的产品
云数据库 Tair(兼容Redis),内存型 2GB
云数据库 MongoDB,独享型 2核8GB
推荐场景:
构建全方位客户视图
云原生多模数据库 Lindorm,多引擎 多规格 0-4节点
简介:

问题背景

对使用 wiredtiger 引擎的 mongod 进行如下测试,不断的『创建集合、创建索引,插入一条记录』,然后统计这3个动作的耗时。

var db = db.getSiblingDB("testdb");
for (var i = 0; i < 100000; i++) {
    var start = (new Date()).getTime();
    var collName = "test" + i;
    var doc = {name: "name" +i, seq: i};
    db.createCollection(collName);        // 创建集合
    db[collName].createIndex({name: 1});  // 创建索引
    db[collName].insert(doc);             // 插入一条记录
    var end = (new Date()).getTime();     // 统计耗时
    print("cost: " + (end - start));
}

随着集合数越来越多,测试过程中发现2个问题

  1. 偶尔会出现耗时很长的请求(1s、2s、3s..不断上升),统计了下频率,大约1分钟左右出现一次。
  2. 平均耗时不断增加,从最开始平均10ms 不到,一直到20ms、30ms、40ms...

测试问题1

因为耗时很长的请求频率大概1分钟一次,跟 wiredtiger 默认的60scheckpoint 很接近,怀疑问题跟 checkpoint 有关,从运行慢日志看,耗时长是因为 createIndex 的原因。

通过当时的 pstack 发现,创建索引的线程正在等锁,只有 checkpoint 线程在干活

Thread 4 (Thread 0x7f80c3c72700 (LWP 70891)):
#0  0x00007f80c2ddc054 in __lll_lock_wait () from /lib64/libpthread.so.0
#1  0x00007f80c2dd7388 in _L_lock_854 () from /lib64/libpthread.so.0
#2  0x00007f80c2dd7257 in pthread_mutex_lock () from /lib64/libpthread.so.0
#3  0x00000000019f3f95 in __wt_curfile_open ()
#4  0x0000000001a580a5 in __session_open_cursor_int ()
#5  0x0000000001a09e13 in __wt_curtable_open ()
#6  0x0000000001a57f29 in __session_open_cursor_int ()
#7  0x0000000001a584b9 in __session_open_cursor ()
#8  0x000000000108cfe9 in mongo::WiredTigerIndex::BulkBuilder::openBulkCursor(mongo::WiredTigerIndex*) ()
#9  0x000000000108841e in mongo::WiredTigerIndexStandard::getBulkBuilder(mongo::OperationContext*, bool) ()
#10 0x0000000000cb09e9 in mongo::IndexAccessMethod::commitBulk(mongo::OperationContext*, std::unique_ptr<mongo::IndexAccessMethod::BulkBuilder, std::default_delete<mongo::IndexAccessMethod::BulkBuilder> >, bool, bool, std::set<mongo::RecordId, std::less<mongo::RecordId>, std::allocator<mongo::RecordId> >*) ()
#11 0x0000000000b07410 in mongo::MultiIndexBlock::doneInserting(std::set<mongo::RecordId, std::less<mongo::RecordId>, std::allocator<mongo::RecordId> >*) ()
#12 0x0000000000b0797d in mongo::MultiIndexBlock::insertAllDocumentsInCollection(std::set<mongo::RecordId, std::less<mongo::RecordId>, std::allocator<mongo::RecordId> >*) ()


Thread 68 (Thread 0x7f80b9336700 (LWP 37085)):
#0  0x00000000019db9e0 in __config_next ()
#1  0x00000000019dc106 in __config_getraw.isra.0 ()
#2  0x00000000019dc5a6 in __wt_config_getones ()
#3  0x0000000001a2437d in __wt_meta_ckptlist_get ()
#4  0x0000000001a65218 in __checkpoint_worker.isra.10 ()
#5  0x0000000001a64888 in __checkpoint_apply ()
#6  0x0000000001a6657a in __txn_checkpoint ()
#7  0x0000000001a66e17 in __wt_txn_checkpoint ()
#8  0x0000000001a57854 in __session_checkpoint ()
#9  0x00000000019e4f8f in __ckpt_server ()
#10 0x00007f80c2dd5851 in start_thread () from /lib64/libpthread.so.0
#11 0x0000003403ee767d in clone () from /lib64/libc.so.6

为什么建索引会跟 checkpoint 有冲突?分析索引代码发现,前台建索引时,mongod 会使用 wiredtiger 的 bulk cursor,而openBulkCursor是要竞争 checkpoint 锁的(个人理解是避免在 bulk insert 过程中出现 checkpoint),所以 createIndex 会阻塞等待 checkpoint 完成。

// src/cursor/cur_file.c:__wt_curfile_open
 /* Bulk handles require exclusive access. */
    if (bulk)
        LF_SET(WT_BTREE_BULK | WT_DHANDLE_EXCLUSIVE);

    /* Get the handle and lock it while the cursor is using it. */
    if (WT_PREFIX_MATCH(uri, "file:")) {
        /*
         * If we are opening exclusive, get the handle while holding
         * the checkpoint lock.  This prevents a bulk cursor open
         * failing with EBUSY due to a database-wide checkpoint.
         */
        if (LF_ISSET(WT_DHANDLE_EXCLUSIVE))
            WT_WITH_CHECKPOINT_LOCK(session, ret,
                ret = __wt_session_get_btree_ckpt(
                session, uri, cfg, flags));

另外从目前的实现看,后台建索引时并不是 bulk cursor,而是使用普通的 cursor 逐条插入,故不会去竞争 checkpoint 的锁,上述测试代码在createIndex 时加上{background: true}选项时问题解决。

建议用户在建立索引时,尽量选择后台建索引的方式,可能性能上不如前台方式,但后台建索引对业务的影响是最小的(前台建索引还会获取 db 的写锁,导致 db 上的读写都被阻塞),最好的方式是 DDL 和 DML 分离,在业务代码中不要出现建索引、建集合的逻辑,预先创建好,业务只做CRUD 操作。

测试问题2

这个问题主要跟文件系统机制相关,testdb 下创建了数万个集合,对应到 wiredtiger 的实现,会出现一个目录下数万个文件的情况(集合的每个索引也要对应一个文件),而从ext4文件系统层面上,在目录里创建文件,先要遍历整个目录下所有的文件项,文件越多效率越低。

上述问题通常的解决方法是『将扁平化的目录层次化』,对应到 mongodb,就是将数万个集合分散到多个 DB 里,具体方法如下。

  1. 配置 storage.directoryPerDB 选项为 true
  2. 业务上将集合分散到多个 DB 里(如100个,平均每个目录下就只有几百个文件)

总结

MongoDB 使用 wiredtiger 引擎时,大量集合的场景(通常业务设计上是有问题的),可能会遇到很多未知的问题,毕竟这不属于常见的应用场景,官方在这方面的测试支持也会相对弱些,比如上述提到的2个问题,还有之前分享的一个集合太多无法同步的问题,建议大家使用 MongoDB 时,合理设计数据模型,避免踩不必要的坑。

相关实践学习
MongoDB数据库入门
MongoDB数据库入门实验。
快速掌握 MongoDB 数据库
本课程主要讲解MongoDB数据库的基本知识,包括MongoDB数据库的安装、配置、服务的启动、数据的CRUD操作函数使用、MongoDB索引的使用(唯一索引、地理索引、过期索引、全文索引等)、MapReduce操作实现、用户管理、Java对MongoDB的操作支持(基于2.x驱动与3.x驱动的完全讲解)。 通过学习此课程,读者将具备MongoDB数据库的开发能力,并且能够使用MongoDB进行项目开发。 &nbsp; 相关的阿里云产品:云数据库 MongoDB版 云数据库MongoDB版支持ReplicaSet和Sharding两种部署架构,具备安全审计,时间点备份等多项企业能力。在互联网、物联网、游戏、金融等领域被广泛采用。 云数据库MongoDB版(ApsaraDB for MongoDB)完全兼容MongoDB协议,基于飞天分布式系统和高可靠存储引擎,提供多节点高可用架构、弹性扩容、容灾、备份回滚、性能优化等解决方案。 产品详情: https://www.aliyun.com/product/mongodb
相关文章
|
6月前
|
网络协议 Shell Linux
【Shell 命令集合 网络通讯 】⭐⭐⭐Linux 测试与目标主机之间的网络连接ping 命令 使用指南
【Shell 命令集合 网络通讯 】⭐⭐⭐Linux 测试与目标主机之间的网络连接ping 命令 使用指南
155 1
|
21天前
|
NoSQL MongoDB 数据库
MongoDB 删除集合
10月更文挑战第14天
25 1
|
8天前
|
缓存 NoSQL MongoDB
|
22天前
|
存储 NoSQL MongoDB
MongoDB 创建集合
10月更文挑战第13天
27 1
|
3月前
|
存储 NoSQL 数据管理
揭秘MongoDB时间序列集合:这个超级功能将如何彻底改变你的数据管理?
【8月更文挑战第8天】时间序列数据记录随时间变化的信息,在数据库管理中至关重要。MongoDB自4.0版起引入时间序列集合,专为这类数据优化存储与查询。通过问答形式介绍其特点:自动数据过期、高效存储机制及快速查询操作。创建时需指定时间字段及可选元数据字段。支持设置数据过期时间,采用粗粒度索引减少I/O操作。查询时可通过时间范围筛选数据,并利用聚合框架进行数据分析。随着实时分析需求的增长,时间序列集合的应用将更加广泛。
156 0
|
6月前
|
NoSQL 测试技术 MongoDB
【MongoDB 专栏】MongoDB 的性能基准测试与评估
【5月更文挑战第11天】MongoDB的性能基准测试对于优化至关重要,涉及数据读写速度、查询响应时间及吞吐量等指标。测试应明确目标和范围,选择合适的工具,考虑数据模型、索引、查询优化和系统配置等因素。性能评估需关注读写吞吐量、响应时间和资源利用率。通过多次测试、逐步增加负载和对比其他系统,识别性能瓶颈并持续优化。随着技术发展,测试方法和工具将持续创新,以应对复杂性能挑战。
278 3
【MongoDB 专栏】MongoDB 的性能基准测试与评估
|
5月前
|
Linux Shell 测试技术
Linux服务器测试脚本集合
LemonBench是iLemonrain创作的Linux服务器性能测试工具,能一键检测系统信息、网络、CPU、内存和硬盘性能。
55 0
|
5月前
|
NoSQL 安全 MongoDB
精准数据清理:掌握 MongoDB 删除集合的方法与最佳实践
精准数据清理:掌握 MongoDB 删除集合的方法与最佳实践
179 0
|
5月前
|
存储 NoSQL MongoDB
MongoDB 集合创建指南:命名规范、索引优化和数据模型设计
MongoDB 集合创建指南:命名规范、索引优化和数据模型设计
104 0
|
5月前
|
Java
JavaSE——集合框架二(6/6)-(案例)补充知识:集合的嵌套(需求与分析、问题解决、运行测试)
JavaSE——集合框架二(6/6)-(案例)补充知识:集合的嵌套(需求与分析、问题解决、运行测试)
77 0

相关产品

  • 云数据库 MongoDB 版