乐趣区

关于mongodb:MongoDB-索引操作引起的-Crash

摘要:本文具体论述了依据引起 Crash 操作进行从配置到源码的剖析过程,层层递进,定位复现并给出解决故障计划。

作者:徐耀荣

爱可生南区交付服务部 DBA 团队成员,次要负责 MySQL 故障解决以及相干技术支持。喜好电影,游览。

本文起源:原创投稿

  • 爱可生开源社区出品,原创内容未经受权不得随便应用,转载请分割小编并注明起源。

故障景象

近日,敌人遇到一个 MongoDB 实例 Crash 的问题,找到我帮忙一起剖析起因,事件通过以及剖析过程如下,可供学习。

操作过程

运维人员在优化慢查问时针对性创立了一个索引,语句如下:

db.c1.createIndex('name':1,background:true)

随后又将表上一个没能用上的索引删除,语句如下:

db.c1.dropIndex('idx_age')

在主节点上很顺利的就实现了,然而不久后就发现从节点产生了 Crash,日志中蕴含下列解体信息。

2023-04-13T07:00:50.752+0000 E STORAGE  [conn3569849] WiredTiger error (-31802) [1681369250:752455][9937:0x7fe740144700], WT_CONNECTION.open_session: __open_session, 2058: out of sessions, configured for 20030 (including internal sessions): WT_ERROR: non-specific WiredTiger error Raw: [1681369250:752455][9937:0x7fe740144700], WT_CONNECTION.open_session: __open_session, 2058: out of sessions, configured for 20030 (including internal sessions): WT_ERROR: non-specific WiredTiger error
2023-04-13T07:00:50.752+0000 I NETWORK  [listener] connection accepted from xxx.xxx.xxx.xxx #3570023 (20576 connections now open)
2023-04-13T07:00:50.753+0000 F -        [conn3569849] Invariant failure: conn->open_session(conn, NULL, "isolation=snapshot", &_session) resulted in status UnknownError: -31802: WT_ERROR: non-specific WiredTiger error at src/mongo/db/storage/wiredtiger/wiredtiger_session_cache.cpp 111

其它信息

  • 变更表是一张几千万的大表;
  • 数据库架构为 MongoDB 4.0.14 的 PSA 架构;
  • 利用开启了读写拆散,从节点也存在大量只读申请。

问题剖析

依据日志信息,初步狐疑是连贯打满了,查看最大连接数配置。

初步排查

shard1:PRIMARY> db.serverStatus().connections;
{"current" : 7, "available" : 29993, "totalCreated" : 7, "active" : 2}

最大连接数是由 maxIncomingConnections 参数和 ulimit 决定的。

net:
  maxIncomingConnections: 30000

在测试环境模拟连接数打满的状况,发现在连接数满了的状况下实例只会回绝新的连贯,而非间接 Crash。

connecting to: mongodb://10.186.64.88:27017/admin?gssapiServiceName=mongodb
2023-04-19T13:59:26.578+0000 I NETWORK   DBClientConnection failed to receive message from 10.186.64.88:27017 - HostUnreachable: Connection closed by peer
2023-04-19T13:59:26.579+0000 E QUERY     Error: network error while attempting to run command 'isMaster' on host '10.186.64.88:27017'  :
connect@src/mongo/shell/mongo.js:344:17
@(connect):2:6
exception: connect failed

依据 SERVER-30462 形容狐疑是 WT_SESSION 打满的状况。

WT_SESSION 是 MongoDB Server 和 WiredTiger 存储引擎外部交互应用的会话,简直所有操作都是在 WT_SESSION 的上下文中执行的。因而 WT_SESSION 在超过限度后将会触发较为重大的状况。

源码剖析

在源码 mongo/wiredtiger_kv_engine.cpp 中能够看到 WT_SESSION 硬编码指定为 20000。

std::stringstream ss;
    ss << "create,";
    ss << "cache_size=" << cacheSizeMB << "M,";
    ss << "cache_overflow=(file_max=" << maxCacheOverflowFileSizeMB << "M),";
    ss << "session_max=20000,";
    ss << "eviction=(threads_min=4,threads_max=4),";
    ss << "config_base=false,";
    ss << "statistics=(fast),";

这一点也能在启动日志中进一步失去验证。

如果 WT_SESSION 数量超过 20000,将会触发 out of sessions 的报错。

    /* Find the first inactive session slot. */
    for (session_ret = conn->sessions, i = 0; i < conn->session_size; ++session_ret, ++i)
        if (!session_ret->active)
            break;
    if (i == conn->session_size)
        WT_ERR_MSG(session, WT_ERROR, "out of sessions, configured for %" PRIu32
                                      "(including"
                                      "internal sessions)",
          conn->session_size);

提出疑难

剖析到这开始纳闷 WT_SESSION 打满与索引操作存在什么样的关系?为什么雷同的操作在主节点能够失常实现,而从节点会产生 Crash?

在创立索引时指定 background:true 能够在后盾构建索引,不会加锁阻塞汇合上的其它操作,这也是咱们日常增加索引罕用的形式。

但在删除索引时,咱们有一点须要留神,但又经常被疏忽,在主节点删除索引后同步到从节点回放时,如果从节点正在跑同一个汇合上后盾创立索引的操作,那么删除索引的操作将会被阻塞,更重大的是这时候实例上所有 namespace 的拜访都将会被阻塞。针对这一景象在官网 dropIndex 文档中有提及:

Avoid dropping an index on a collection while any index is being replicated on a secondary. If you attempt to drop an index from a collection on a primary while the collection has a background index building on a secondary, reads will be halted across all namespaces and replication will halt until the background index build completes.

当任何创立索引操作复制到 Secondary 时,应防止在汇合上删除索引。如果你试图在 Primary 上删除一个索引,而该汇合在 Secondary 上有一个索引正在后盾创立,那么所有 namespace 的拜访将被进行,复制也会进行,直到后盾索引建设实现。

回到谬误日志中查找更多内容,就能发现从节点在后盾创立索引时,又执行了同一个汇合上的删除索引操作。

2023-04-13T05:34:27.002+0000 I - [repl index builder 178] Index Build (background): 122873800/640018757 19% 
2023-04-13T05:34:30.002+0000 I - [repl index builder 178] Index Build (background): 122976300/640018769 19% 
2023-04-13T05:34:30.434+0000 I COMMAND [repl writer worker 11] CMD: dropIndexes test.c1

初步论断

到此,咱们得出初步论断。事件起因是主节点在同一个汇合上执行创立索引和删除索引后,在从节点回放时呈现了很重大的阻塞,大量的只读申请开始一直积压,最初导致 WT_SESSION 耗费殆尽,Server 无奈与 WiredTiger 进行外部通信,最终导致实例 Crash。

问题复现

上面的案例在测试环境复现 WT_SESSION 超过限度的状况,dropIndex 导致从节点锁阻塞的问题有趣味可本人测试复现,这里就不做演示了。

WT_SESSION 下限是由 wiredtiger_open 配置中的 session_max 决定的,但 MongoDB 并未间接裸露 session_max 的 配置形式,只能通过下列形式进行笼罩设置。

mongod -f /etc/mongod.conf --wiredTigerEngineConfigString="session_max=5"

而后在数据库外部发动一个全局排它锁。

mongo> db.fsyncLock()

编写下列 Python 脚本模仿并发线程。

#!/usr/bin/python
# -*- coding: UTF-8 -*-
import multiprocessing
import pymongo

def find():
    cnx_args = dict(username='root', password='abcd123#', host='127.0.0.1', port=27018, authSource='admin')
    client=pymongo.MongoClient(**cnx_args)
    db=client['test']
    results=db.tab100.insert_one({"name":"jack"})
if __name__ == "__main__":
    x=1
    while x<350:
        p=multiprocessing.Process(target=find)
        p.start()
        print("start thread:",x)
        x+=1
    p.join()

这时 MongoDB 实例还在失常运行,因为咱们的申请还没有真正的进入到 WiredTiger 引擎层,但一旦咱们手动开释排它锁,所有申请都会在短时间内进入 WiredTiger 引擎,WT_SESSION 霎时超过限度,实例紧接着产生 Crash。

mongo> db.fsyncUnlock()

谬误日志如下,与生产日志雷同。

总结

  1. net.maxIncomingConnections 设置应小于 WT_SESSION;
  2. 能够依据理论需要调整游标超时工夫,避免出现大面积积压的状况;
  3. 防止创立索引和删除索引先后执行,特地是先执行后盾创立索引的状况下;
  4. 4.2 版本中废除了 background 选项,对索引创立过程进行了优化,只会在索引创立的开始和完结时持有 exclusive lock;并且 4.0 版本官网曾经进行提供服务了,倡议尽快降级。

本文关键字:#MongoDB# #WiredTiger# #源码 #

对于 SQLE

爱可生开源社区的 SQLE 是一款面向数据库使用者和管理者,反对多场景审核,反对标准化上线流程,原生反对 MySQL 审核且数据库类型可扩大的 SQL 审核工具。

SQLE 获取

类型 地址
版本库 https://github.com/actiontech/sqle
文档 https://actiontech.github.io/sqle-docs/
公布信息 https://github.com/actiontech/sqle/releases
数据审核插件开发文档 https://actiontech.github.io/sqle-docs-cn/3.modules/3.7_audit…
退出移动版