[toc]
## 索引
索引 字段较少 且是有序的
索引是特殊的数据结构,索引存储在一个易于遍历读取的数据集合中,索引是对数据库表中一列或多列的值进行排序的一种结构
特殊的数据结构,按顺序保存文档中的一个或多个字段
### ensureIndex({xx:1})
#### 默认行为
![](https://box.kancloud.cn/33a9ebdd00f969d24f33f7218dfa1fdf_442x168.png)
默认系统会自动帮我们创建一个`_id`的索引
![](https://box.kancloud.cn/7a7615cdcf159bb72e115e826f2208ef_322x320.png)
1是升序,-1是降序
![](https://box.kancloud.cn/4b5c1c1fdc7212c746e7d8183560e858_282x58.png)
当我们创建索引时弱没有取名字,系统会自动生成,比如你建了一个索引叫`xx`
会自动生成索引名字叫`xx_1`(升序) or `xx_-1`(降序)
#### 具名索引
也可以自己生成一个索引名
```
db.students.ensureIndex({name:1},{name:'namedIndex'});
```
![](https://box.kancloud.cn/46f198de84c7348152b621064a911320_332x438.png)
#### unique
以下栗子中id这个字段的值在每个文档中不可重复
![](https://box.kancloud.cn/580d20bc7bc579eb54b8ce090963293e_493x42.png)
##### 创建唯一索引并删除重复记录
dropDups,貌似新版mongoDB中已废弃
```
db.person.ensureIndex({ "name" : -1 },{ "name" : "indexname", "unique" : true,dropDups:true })
```
#### 支持对数组类型的值的key进行索引
```
db.students.insert({hobby:['basketball','football','pingpang']});
db.students.ensureIndex({hobby:1});
db.students.find({hobby:'football'},{hobby:1,_id:0}).explain(true);
```
#### 在后台创建索引
默认索引创建时是阻断用户继续输入的命令的
```
db.students.ensureIndex({name:1},{name:'nameIndex',unique:true,background:true});
```
#### 过期索引
过期过后,下一次跑任务的时候才会删掉
```
db.stus.insert({time:new Date()});
db.stus.ensureIndex({time:1},{expireAfterSeconds:10});
db.stus.find();
```
1. 索引字段的值必须Date对象,不能是其它类型比如时间戳
2. 删除时间不精确,每60秒跑一次。删除也要时间,所以有误差。
#### 复合索引
查询的条件不止一个,需要用复合索引
```
db.students.ensureIndex({name:1,age:1});
db.students.find({name:1,age:2},{name:1,age:1,_id:0}).explain(true);
```
#### 全文索引
大篇幅的文章中搜索关键词,MongoDB为我们提供了全文索引
假若我们有以下数据
```
db.article.insert({content:'I am a gir'});
db.article.insert({content:'I am a boy'});
```
首先添加索引(开启全文检索)
![](https://box.kancloud.cn/be9f2f4673bb8600ce2ddff2cb03959e_420x118.png)
- $text:表示要在全文索引中查东西
- $search:后边跟查找的内容, 默认全部匹配
```
db.article.find({$text:{$search:'boy'}});
db.article.find({$text:{$search:'girl'}});
db.article.find({$text:{$search:'boy girl'}});//多次查找,多个关键字为或的关系
db.article.find({$text:{$search:"a b"}});
db.article.find({$text:{$search:"boy -girl"}}); // -表示取消
db.article.find({$text:{$search:"a \"coco cola\" b "}}); //支持转义符的,用\斜杠来转义
```
其中`\"coco cola\"`表示一个整体
#### 二维索引
![](https://box.kancloud.cn/a564d64f5f7736d5c551633d05b54c32_222x220.png)
```
db.maps.ensureIndex({gis:'2d'},{min:1,max:3});
默认会创建一个2D索引
```
![](https://box.kancloud.cn/a7a7b3dc6c9e68a677225e45db677b56_405x234.png)
##### 查询处理点x最近的n个点
```
db.maps.find({gis:{$near:[1,1]}}).limit(3);
```
![](https://box.kancloud.cn/65d6ed1ca382c5b454781dbd7d775369_536x73.png)
##### 查询以点()和()为对角线的正方形的所有点
```
db.map.find({gis:{$within:{$box:[[1,1],[2,2]]}}},{_id:0,gis:1});
```
![](https://box.kancloud.cn/eaa83f7988b3e0f3e478858ab5b9cf34_537x85.png)
##### 查出以圆心
```
db.maps.find({gis:{$within:{$center:[[2,2],1]}}},{_id:0,gis:1});
```
### dropIndex()
![](https://box.kancloud.cn/ed01eb4796d710c28543eaf46ab9248c_365x32.png)
### 索引使用的注意事项
- 1为正序 -1为倒序
- 索引虽然可以提升查询性能,但会降低插件性能,对于插入多查询少不要创索引
- 数据量不大时不需要使用索引。性能的提升并不明显,反而大大增加了内存和硬盘的消耗。
- 查询数据超过表数据量30%时,不要使用索引字段查询
- 排序工作的时候可以建立索引以提高排序速度
- 数字索引,要比字符串索引快的多
## 监控查询过程
MongoDB 提供了一个 explain 命令让我们获知系统如何处理查询请求。利用 explain 命令,我们可以很好地观察系统如何使用索引来加快检索,同时可以针对性优化索引。
将`explain`置为`true`
```
db.students.find({age:299999}).explain(true);
```
- cursor: 返回游标类型
- BasicCursor而没有使用索引的查询并不是胡乱查询,而是使用了基本游标:,同理,
- 使用索引的查询就是BtreeCursor
- nscanned: 查找过的索引条目的数量
- n: 返回的文档数量
- nscannedObjects :数据库按照索引去磁盘上查找实际文档的次数
- millis: 执行本次查询所花费的时间,以毫秒计算,这也是判断查询效率的一个重点
- indexBounds: 描述索引的使用情况
- isMultiKey:是否使用了多键索引
- scanAndOrder: 是否在内存中对结果进行了排序
- indexOnly:是否仅仅使用索引完成了本次查询
---
queryPlanner分析
- queryPlanner: queryPlanner的返回
- queryPlanner.namespace:该值返回的是该query所查询的表
- queryPlanner.indexFilterSet:针对该query是否有indexfilter
- queryPlanner.winningPlan:查询优化器针对该query所返回的最优执行计划的详细内容。
- queryPlanner.winningPlan.stage:最优执行计划的stage,这里返回是FETCH,可以理解为通过返回的index位置去检索具体的文档(stage有数个模式,将在后文中进行详解)。
- queryPlanner.winningPlan.inputStage:用来描述子stage,并且为其父stage提供文档和索引关键字。
- queryPlanner.winningPlan.stage的child stage,此处是IXSCAN,表示进行的是index scanning。
- queryPlanner.winningPlan.keyPattern:所扫描的index内容,此处是did:1,status:1,modify_time: -1与scid : 1
- queryPlanner.winningPlan.indexName:winning plan所选用的index。
- queryPlanner.winningPlan.isMultiKey是否是Multikey,此处返回是false,如果索引建立在array上,此处将是true。
- queryPlanner.winningPlan.direction:此query的查询顺序,此处是forward,如果用了.sort({modify_time:-1})将显示backward。
- queryPlanner.winningPlan.indexBounds:winningplan所扫描的索引范围,如果没有制定范围就是[MaxKey, MinKey],这主要是直接定位到mongodb的chunck中去查找数据,加快数据读取。
- queryPlanner.rejectedPlans:其他执行计划(非最优而被查询优化器reject的)的详细返回,其中具体信息与winningPlan的返回中意义相同,故不在此赘述。
---
对executionStats返回逐层分析
- 第一层,executionTimeMillis
- 最为直观explain返回值是executionTimeMillis值,指的是我们这条语句的执行时间,这个值当然是希望越少越好。
- 其中有3个executionTimeMillis,分别是:
- executionStats.executionTimeMillis 该query的整体查询时间。
- executionStats.executionStages.executionTimeMillisEstimate
- 该查询根据index去检索document获得2001条数据的时间。
- executionStats.executionStages.inputStage.executionTimeMillisEstimate
- 该查询扫描2001行index所用时间。
- 第二层,index与document扫描数与查询返回条目数 这个主要讨论3个返回项,nReturned、totalKeysExamined、totalDocsExamined,分别代表该条查询返回的条目、索引扫描条目、文档扫描条目。 这些都是直观地影响到executionTimeMillis,我们需要扫描的越少速度越快。 对于一个查询,我们最理想的状态是:nReturned=totalKeysExamined=totalDocsExamined
- 第三层,stage状态分析 那么又是什么影响到了totalKeysExamined和totalDocsExamined?是stage的类型。类型列举如下:
- COLLSCAN:全表扫描
- IXSCAN:索引扫描
- FETCH:根据索引去检索指定document
- SHARD_MERGE:将各个分片返回数据进行merge
- SORT:表明在内存中进行了排序
- LIMIT:使用limit限制返回数
- SKIP:使用skip进行跳过
- IDHACK:针对_id进行查询
- SHARDING_FILTER:通过mongos对分片数据进行查询
- COUNT:利用db.coll.explain().count()之类进行count运算
- COUNTSCAN:count不使用Index进行count时的stage返回
- COUNT_SCAN:count使用了Index进行count时的stage返回
- SUBPLA:未使用到索引的$or查询的stage返回
- TEXT:使用全文索引进行查询时候的stage返回
- PROJECTION:限定返回字段时候stage的返回
- 对于普通查询,我希望看到stage的组合(查询的时候尽可能用上索引):
- Fetch+IDHACK
- Fetch+ixscan
- Limit+(Fetch+ixscan)
- PROJECTION+ixscan
- SHARDING_FITER+ixscan
- COUNT_SCAN
- 不希望看到包含如下的stage:
- COLLSCAN(全表扫描),SORT(使用sort但是无index),不合理的SKIP,SUBPLA(未用到index的$or),COUNTSCAN(不使用index进行count)
## 配置主从服务器
```
master.conf
```
![](https://box.kancloud.cn/3a98f4d88e586ca263dd90a8d7e44d7b_254x115.png)
```
slave.conf
```
![](https://box.kancloud.cn/13c24bb501f6ff7ef0a6da96ebcd9344_208x117.png)
默认情况下只有主服务器才能读
![](https://box.kancloud.cn/7fe5b1ebd2c850540462805259b23c4e_417x173.png)
或则可以输入命令`rs.slaveOk()`
![](https://box.kancloud.cn/1eeaebeaa795311cf0e736ab06f7b61b_566x110.png)
弊端:不能自主切换
### 主从复制的其它设置项
- -only 从节点-> 指定复制某个数据库默认是复制全部数据库
- -slavedelay 从节点-> 设置主数据库同步数据的延迟(单位是秒)
- -fastsync 从节点-> 以主数据库的节点快照为节点启动从数据库
有可能从服务器不是一开始就加入的而是后面才加入的,主服务器上已经有数据了,这时我们可以给主服务器拍个快照,强行把缓存区内容写入到硬盘上然后同步到从服务器上
- -autoresync 从节点->如果不同步则重新同步数据库
- -oplogSize 主节点->设置oplog的大小(主节点操作记录存储到local的oplog中)
![](https://box.kancloud.cn/5a287e98578a78e80ffed5f3dc7d6494_285x133.png)
### 查看从服务器数据来源
![](https://box.kancloud.cn/a053cee94ec8f72c714b0e4c9c767848_668x63.png)
### 删除添加从服务器数据来源
![](https://box.kancloud.cn/71375765eb6389e72cb550979ad76a69_270x48.png)
![](https://box.kancloud.cn/87d0726c83c46d1113927955f1146704_393x47.png)
## 副本集
### 运行流程
- 一台活跃服务器和二个备份服务器
- 当活跃服务器出现故障,这时集群根据权重算法推选出出活跃服务器
- 当原来的主服务器恢复后又会变成从服务器
### 配置副本集
A服务器
```
dbpath=E:\repl\repl1
port=2001
replSet=group
```
B服务器
```
dbpath=E:\repl\repl2
port=2002
replSet=group
```
C服务器
```
dbpath=E:\repl\repl3
port=2003
replSet=group
```
>**注意:** `replSet`设置了值一样才能作为一个副本集存在,设置了相同值的副本集为同一个`group`
依次按配置文件启动服务器
### 初始化副本集
- rs.initiate() 启动一个新的副本集
- rs.conf() 查看副本集的配置
- rs.status() 命令
启动服务器后,我们还不知道我们的小伙伴具体是谁,要配置一下(随便链接一个服务器)
![](https://box.kancloud.cn/cdd9e4626e031a6476611b513be59ec4_377x179.png)
![](https://box.kancloud.cn/9d728b1d3fa44442d1080743ce6224da_607x234.png)
>**注意:** 不是PRIMARY 就是 SECONDARY 么有THIDARY什么的
当PRIMARY挂掉后,其它副本集会选举出一个新的`PRIMARY`,注意,当原本的primary重连后会成为secondary
### 高级参数
- standard 常规节点 参与投票有可能成为活跃节点
- passive 副本节点 参与投票,但不能成为活跃节点
- arbiter 仲裁节点 只参与投票,不复制节点,也不能成为活跃节点
- priority 0到1000之间,0代表是副本节点,1到1000是常规节点
- arbiterOnly:true 仲裁节点
### 读写分离操作
一般情况下作为副本节点是不能进行数据库操作的,但是在读取密集的系统中读写分离是必要的
### Oplog
它被存储在本地数据库local中,会记录每一个操作。 如果希望在故障恢复的时候尽可能更多,可以把这个size设置的大一点
默认5% disk
```
--oplogSize 1024
use local;
db.oplog.rs.find().limit(2);
```
## 官方文档
- [点我](https://docs.mongodb.com/manual/reference/configuration-options/)