生产事故(MongoDB数据分布不均解决方案)

2024-06-07 18:58

本文主要是介绍生产事故(MongoDB数据分布不均解决方案),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

事故集合:

可以很明显可以看到我们这个集合的数据严重分布不均匀。

一共有8个分片,面对这个情况我首先想到的是手动拆分数据块,但这不是解决此问题的根本办法。

    造成此次生产事故的首要原因就是片键选择上的问题,由于片键选择失误,在数据量级不大的时候数据看起来还是很健康的,但随着数据量的暴涨,问题就慢慢浮出了水面,我们使用的组合片键并不是无规律的,片键内容是线性增长的,这就导致了数据的不正常聚集。由于数据分布不均匀,我们有两个分片的磁盘使用率接近80%,数据还在持续增长,这个问题必须尽快解决。

涉及到此次事故的集合一共有三个,总数据量加起来接近30T,数据总量300亿左右。

下面是我解决此问题的解决方案:

方案一:

第一步:创建一个新的分片表,片键我选择_id做hashed分片,并提前分好了数据块,降低在恢复期间频繁切割数据造成的服务器压力。

sh.shardCollection("loan_his.collection",{_id:"hashed"},false,{numInitialChunks:1024})

第二步:单独连接各个分片将8个分片的数据全量备份:

nohup mongodump -u loan_his -p loan_his --authenticationDatabase loan_his  -h ${replset} --db loan_his --collection ${collectionName} --query '{"txdt": { $lte: "2019-07-09"} }' -o ${bak_dir} &>>  ${log} &

你可能会问为什么不连接mongos,因为我在连接mongos做数据备份时出现了以下异常:

2019-07-08T16:10:03.886+0800	Failed: error writing data for collection `loan_his.ods_cus_trad` to disk: error reading collection: operation was interrupted

可能是因为集合内的数据坏块吧,此异常信息是我备份了将近70%的数据后突然抛出的异常信息。

除了这个原因,单独备份各个分片的数据后你能够自由控制恢复数据的时间窗口,不会因为恢复单个数据文件时间较长,突发意外情况导致恢复中断从头再来的窘境。能够根据服务器的状态避开高峰期来进行数据恢复。

备份期间我发现了有时候备份出来的总文档数和 db.collection.getShardDistribution() 查看的文档数不一致,我还以为是备份期间出了问题,但我删除当前备份文件后重新备份出来的文档数还是和之前一样。目前不知道是怎么回事,怀疑是坏的数据块引发的我问题,备份出来的数据一般会比原数据量多几万条数据,有时候会少一些。

第三步:恢复数据:

 mongorestore -u loan_his -p loan_his --authenticationDatabase loan_his -h 10.0.156.9:27017 --db loan_his  --collection  ${collectionName_two}  /mongodb/${collectionName}/replset_sh2/loan_his/${collectionName}.bson  &>>  ${log}

在恢复数据前千万要记得不要创建索引!否则性能极差,速度非常非常慢!在使用mongodump工具备份时,在数据文件的同级目录下会有一个 XXXXX.metadata.json 索引文件,默认会在数据恢复完毕后执行创建索引的操作。

此处有坑需要注意:因为备份出来的数据是由原表备份出来的,那这个索引文件也是原表的索引,由于原表我使用的是组合片键做的分片,所以在原表内会存在一个由片键组成的组合索引,并且不是后台创建的组合索引!!!这意味着如果你使用此索引文件来给新表创建索引,会造成这个集群处于阻塞状态,无法响应任何操作!!直至索引创建完毕。所以你可以将这个索引文件备份到其它目录以作参考,然后将原文件删除就可以了,恢复数据时不会有其它的问题。

如果恢复期间出现了意外情况导致恢复失败,比如节点宕机什么的,不需要担心,重新执行恢复程序,数据文件不会重复增加,因为备份出来的数据文件包含mongodb自带的 Objectld对象_id ,导入时,如果已存在此ID,将不会插入数据。注意:在不同集合是允许出现相同ID的,所以在使用方案二恢复数据时,新产生的数据不能通过新表A备份出来汇入新表C,需要通过原始数据文件重新导入。

第四步:创建索引:

待所有数据恢复完毕后再创建索引,一定要记得后台创建!!!"background":true !!!你也可以将索引拆分,一个一个的来。如果觉得此操作对业务影响较大,请看本文最后的解决方案。

mongo 10.0.156.2:27017/loan_his -uloan_his -ploan_his -eval 'db.getSiblingDB("loan_his").runCommand({createIndexes: "collection",indexes: [{"v":2,"key":{"_id":1},"name":"_id_","ns":"loan_his.collection"},{"v":2,"key":{"opnode":1.0,"txdt":1.0,"acct":1.0,"crdno":1.0},"name":"opnode_1_txdt_1_acct_1_crdno_1","ns":"loan_his.collection","background":true},{"v":2,"key":{"txdt":1.0,"opnode":1.0,"acct":1.0,"crdno":1.0,"pbknum":1.0},"name":"txdt_1_opnode_1_acct_1_crdno_1_pbknum_1","ns":"loan_his.collection","background":true},{"v":2,"key":{"acct":1.0,"txdt":1.0,"opnode":1.0},"name":"acct_1_txdt_1_opnode_1","ns":"loan_his.collection","background":true},{"v":2,"key":{"crdno":1.0,"txdt":1.0,"opnode":1.0},"name":"crdno_1_txdt_1_opnode_1","ns":"loan_his.collection","background":true},{"v":2,"key":{"pbknum":1.0,"txdt":1.0,"opnode":1.0},"name":"pbknum_1_txdt_1_opnode_1","ns":"loan_his.collection","background":true}]})'

停止失控索引:

一旦你触发一个索引,简单的重启服务并不能解决这个问题,因为MongoDB会继续重启前的建索引的工作。如果之前你运行后台建索引任务,在服务重启后它会变成前台运行的任务。在这种情况下,重启会让问题变得更糟糕。MongoDB提供了选项“noIndexBuildRetry”,它会指示MongoDB重启后不再继续没建完的索引。如果不小心在前台创建了索引导致集群不可用,可以使用--noIndexBuildRetry 参数重启各个分片来停止索引的创建过程,只用重启主节点就可以了。如果是在后台创建索引,重启时记得加上--noIndexBuildRetry,否则重启后创建索引的线程会重新被唤醒,并由后台创建变为前台创建,导致整个集群不可用。

mongod -f $CONFIGFILE --noIndexBuildRetry

此方案迁移期间不用通知业务系统做变更,把数据迁移完毕后,通知业务系统将表名变更,弊端就是在你迁移的过程中数据还是会持续增长的,问题分片的磁盘容量会越来越少。

方案二:

为了避免在迁移期间数据仍在增长,导致数据还没迁移完毕磁盘就爆满的情况,可以选择停止往旧表B内写入数据,创建一个健康的新表A,新的数据往新表A内写,具体的查询方案需要应用系统的配合。然后将旧表B的数据迁移至新表C中,最终将新表A的数据汇入新表C , 完成数据迁移。此次迁移数据耗时共9个月!!!片键一定要慎重选择,因为我们使用的MongoDB是3.4.7版本的,不支持修改片键,最新版本支持片键的修改。

接下来介绍数据量较大时如何构建索引--减少业务最少影响

在数据量较大或请求量较大,直接建立索引对性能有显著影响时,可以利用复制集(数据量较大时一般为线上环境,使用复制集为必然选择或者使用分片.)中部分机器宕机不影响复制集工作的特性,继而建立索引。

(1)首先把 secondary server 停止,再注释 --replSet 参数,并且更改 MongoDB port 之后重新启动 MongoDB,这时候 MongoDB 将进入 standalone 模式;

(2).在 standalone 模式下运行命令 ensureIndex 建立索引,使用 foreground 方式运行也可以,建议使用background方式运行;

(3)建立索引完毕之后关闭 secondary server 按正常方式启动;

4.根据上述 1~3 的步骤轮流为 secondary 建立索引,最后把 primary server 临时转换为 secondary server,同样按 1~3 的方法建立索引,再把其转换为 primary server。

日志内容大致如下:

2019-09-24T18:51:39.003+0800 I -        [conn33]   Index Build: 838416900/876543270 95%
2019-09-24T20:10:08.360+0800 I INDEX    [conn33] 	 done building bottom layer, going to commit
2019-09-24T20:10:26.001+0800 I -        [conn33]   Index: (2/3) BTree Bottom Up Progress: 11684400/876543270 1%
done building bottom layer, going to commit

 

 

这篇关于生产事故(MongoDB数据分布不均解决方案)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1040016

相关文章

Linux samba共享慢的原因及解决方案

《Linuxsamba共享慢的原因及解决方案》:本文主要介绍Linuxsamba共享慢的原因及解决方案,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录linux samba共享慢原因及解决问题表现原因解决办法总结Linandroidux samba共享慢原因及解决

找不到Anaconda prompt终端的原因分析及解决方案

《找不到Anacondaprompt终端的原因分析及解决方案》因为anaconda还没有初始化,在安装anaconda的过程中,有一行是否要添加anaconda到菜单目录中,由于没有勾选,导致没有菜... 目录问题原因问http://www.chinasem.cn题解决安装了 Anaconda 却找不到 An

Spring定时任务只执行一次的原因分析与解决方案

《Spring定时任务只执行一次的原因分析与解决方案》在使用Spring的@Scheduled定时任务时,你是否遇到过任务只执行一次,后续不再触发的情况?这种情况可能由多种原因导致,如未启用调度、线程... 目录1. 问题背景2. Spring定时任务的基本用法3. 为什么定时任务只执行一次?3.1 未启用

MySQL新增字段后Java实体未更新的潜在问题与解决方案

《MySQL新增字段后Java实体未更新的潜在问题与解决方案》在Java+MySQL的开发中,我们通常使用ORM框架来映射数据库表与Java对象,但有时候,数据库表结构变更(如新增字段)后,开发人员可... 目录引言1. 问题背景:数据库与 Java 实体不同步1.1 常见场景1.2 示例代码2. 不同操作

java常见报错及解决方案总结

《java常见报错及解决方案总结》:本文主要介绍Java编程中常见错误类型及示例,包括语法错误、空指针异常、数组下标越界、类型转换异常、文件未找到异常、除以零异常、非法线程操作异常、方法未定义异常... 目录1. 语法错误 (Syntax Errors)示例 1:解决方案:2. 空指针异常 (NullPoi

使用DrissionPage控制360浏览器的完美解决方案

《使用DrissionPage控制360浏览器的完美解决方案》在网页自动化领域,经常遇到需要保持登录状态、保留Cookie等场景,今天要分享的方案可以完美解决这个问题:使用DrissionPage直接... 目录完整代码引言为什么要使用已有用户数据?核心代码实现1. 导入必要模块2. 关键配置(重点!)3.

Jackson库进行JSON 序列化时遇到了无限递归(Infinite Recursion)的问题及解决方案

《Jackson库进行JSON序列化时遇到了无限递归(InfiniteRecursion)的问题及解决方案》使用Jackson库进行JSON序列化时遇到了无限递归(InfiniteRecursi... 目录解决方案‌1. 使用 @jsonIgnore 忽略一个方向的引用2. 使用 @JsonManagedR

Seata之分布式事务问题及解决方案

《Seata之分布式事务问题及解决方案》:本文主要介绍Seata之分布式事务问题及解决方案,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录Seata–分布式事务解决方案简介同类产品对比环境搭建1.微服务2.SQL3.seata-server4.微服务配置事务模式1

关于Nginx跨域问题及解决方案(CORS)

《关于Nginx跨域问题及解决方案(CORS)》文章主要介绍了跨域资源共享(CORS)机制及其在现代Web开发中的重要性,通过Nginx,可以简单地解决跨域问题,适合新手学习和应用,文章详细讲解了CO... 目录一、概述二、什么是 CORS?三、常见的跨域场景四、Nginx 如何解决 CORS 问题?五、基

Nginx启动失败:端口80被占用问题的解决方案

《Nginx启动失败:端口80被占用问题的解决方案》在Linux服务器上部署Nginx时,可能会遇到Nginx启动失败的情况,尤其是错误提示bind()to0.0.0.0:80failed,这种问题通... 目录引言问题描述问题分析解决方案1. 检查占用端口 80 的进程使用 netstat 命令使用 ss