# 对mongo分片集群创建索引

### 1. 场景
现在我们需要给大规模的mongodb集群添加索引，当集群规模巨大的时候,创建索引是一个非常可怕的操作，因为一个索引的创建的过程非常长，因为即使是稀疏索引也是需要遍历现存的所有数据的才能完成的，而且创建索引时间比较漫长的，可能在中间出现创建索引的节点宕机什么的问题；而且创建索引本身是不一个不可逆的过程，创建完之后马上删除索引是会出现很严重的问题；为此对于这个场景我们需要思考更加多一些；
### 2. 方案
mongo的集群模式包含有`mongos + mongod replication`，创建索引肯定不能直接从mongos下发，虽然从理论上可行，但是如果出现其他的没有考虑的情况的话，到时候影响面就会很巨大的；所以我们的方案基本上是这样的：
```
1. 一组一组shard进行创建索引的; 将db对应的primaryId对应shard放在最后去创建索引;
2. 全部完成之后再在mongos在统一创建索引;
真实创建索引是在mongod段的，所以mongos最后的创建索引是不会有任何影响，并且不会有副作用的；
```
#### 2.1 知识点: 每一个db都会存在一个primary节点
在做实验的过程中，上面的第一步做完之后，从mongos段能通过`getIndexes`这个函数看到对应的索引，这非常奇怪，因为我本身没有对mongos进行索引创建的；最终在分析mongos的也源码发现，发现这个索引信息是去`db的PrimaryId`对应的shard上获得的，这个primaryId的信息要如何获得呢？
```shell
use config
db.databases.find()
//结果
{ "_id" : "test_LCC", "primary" : "shard1", "partitioned" : true }
{ "_id" : "exporter", "primary" : "shard2", "partitioned" : true }
```
**结论:** 只要对primaryId的shard创建了索引，那么mongos就能看到对应的索引；所以在创建索引的时候需要将其放在最后来创建索引，主要是为了安全性，怕遇到未知的行为;
#### 2.2 扩容集群是否需要手动创建索引
```
测试结果是不需要，因为当进行movechunk的时候，mongodb会创建对应的db/coll/对应的索引
```
### 3. 预案
目前主要集中在副本集内部的创建索引的故障的场景预案上;
#### 3.1 `primary`在创建索引的时候挂了怎么办
目前mongodb 副本集创建索引是primary先完成，然后secondary会并发创建索引的； 所以在当前场景下，primary挂了就切主，新的primary理论上不会知道要创建索引，而老的primary会重启然后开始恢复创建索引的进度，并且不会启动端口提供服务; 所以针对这个场景的步骤:
```
1. 观察新的primary是否能提供正常的服务; 
2. 观察老的primary重启之后是不是在进行索引恢复过程中; 关键字为:"Index Build" 会出现类似的"Index Build (background): 235900/319691570 0%" 如果这个日志滚动中那么表示节点正在创建索引;
3. 等到老的primary创建索引成功之后,关键字为:"build index done"，会有类似的日志:"build index done. scanned 324427514 total records. 4909 secs" 
4. 如果老的primary创建成功之后，先去新的primary查看对应的collection是否有创建索引，正常情况是不会有的；这个时候把之前创建索引的语句在新primary重新执行
5. 观察服务的稳定性吧
```
##### 3.2 primary创建成功之后，secondary并行创建过程中宕机
关于这个场景需要分开两种情况来聊：
* secondary宕机不影响majority写
```
1. 重启secondary观察创建索引的进度即可;
```
* 多个secondary出现宕机开始影响到majority写的时候
```
1. 关闭正在恢复索引的secondary
2. 启动参数加入"--noIndexBuildRetry"，这样让secondary忽略恢复索引的过程的；直接起来提供服务
3. 等整个副本集对外写入稳定的时候，这个时候可能的情况是集群内部有几个节点存在索引，有几个节点没有的；
4. 在保证副本集副本数安装的情况下，找一个有索引的副本进行锁库，然后将数据同步给其他的没有索引的节点，
5. 数据同步完之后，启动;
6. 最后要将"--noIndexBuildRetry"参数删除掉
```
##### 3.3 primary 创建索引过程中，性能影响很大的
```
1. 切主，将主迁移到其他的节点上
2. 找研发讨论后期应该如何处理
```
##### 3.4 primary创建成功，但是secondary并行创建的性能抖动很大的
```
1. 选择几个secondary节点，修改启动参数，加入"--noIndexBuildRetry",然后重启；保证不会对写入有影响
2. 保留一个或者多个节点进行索引创建
3. 创建成功之后，通过数据copy的方式来让那些没有索引的节点也有索引
```
