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

事故集合:node

能够很明显能够看到咱们这个集合的数据严重分布不均匀。mongodb

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

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

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

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

方案一:性能

第一步:建立一个新的分片表,片键我选择_id作hashed分片,并提早分好了数据块,下降在恢复期间频繁切割数据形成的服务器压力。ui

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作数据备份时出现了如下异常:rest

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
相关文章
相关标签/搜索