[toc]
## 索引
索引 字段較少 且是有序的
索引是特殊的數據結構,索引存儲在一個易于遍歷讀取的數據集合中,索引是對數據庫表中一列或多列的值進行排序的一種結構
特殊的數據結構,按順序保存文檔中的一個或多個字段
### ensureIndex({xx:1})
#### 默認行為

默認系統會自動幫我們創建一個`_id`的索引

1是升序,-1是降序

當我們創建索引時弱沒有取名字,系統會自動生成,比如你建了一個索引叫`xx`
會自動生成索引名字叫`xx_1`(升序) or `xx_-1`(降序)
#### 具名索引
也可以自己生成一個索引名
```
db.students.ensureIndex({name:1},{name:'namedIndex'});
```

#### unique
以下栗子中id這個字段的值在每個文檔中不可重復

##### 創建唯一索引并刪除重復記錄
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'});
```
首先添加索引(開啟全文檢索)

- $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\"`表示一個整體
#### 二維索引

```
db.maps.ensureIndex({gis:'2d'},{min:1,max:3});
默認會創建一個2D索引
```

##### 查詢處理點x最近的n個點
```
db.maps.find({gis:{$near:[1,1]}}).limit(3);
```

##### 查詢以點()和()為對角線的正方形的所有點
```
db.map.find({gis:{$within:{$box:[[1,1],[2,2]]}}},{_id:0,gis:1});
```

##### 查出以圓心
```
db.maps.find({gis:{$within:{$center:[[2,2],1]}}},{_id:0,gis:1});
```
### dropIndex()

### 索引使用的注意事項
- 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
```

```
slave.conf
```

默認情況下只有主服務器才能讀

或則可以輸入命令`rs.slaveOk()`

弊端:不能自主切換
### 主從復制的其它設置項
- -only 從節點-> 指定復制某個數據庫默認是復制全部數據庫
- -slavedelay 從節點-> 設置主數據庫同步數據的延遲(單位是秒)
- -fastsync 從節點-> 以主數據庫的節點快照為節點啟動從數據庫
有可能從服務器不是一開始就加入的而是后面才加入的,主服務器上已經有數據了,這時我們可以給主服務器拍個快照,強行把緩存區內容寫入到硬盤上然后同步到從服務器上
- -autoresync 從節點->如果不同步則重新同步數據庫
- -oplogSize 主節點->設置oplog的大小(主節點操作記錄存儲到local的oplog中)

### 查看從服務器數據來源

### 刪除添加從服務器數據來源


## 副本集
### 運行流程
- 一臺活躍服務器和二個備份服務器
- 當活躍服務器出現故障,這時集群根據權重算法推選出出活躍服務器
- 當原來的主服務器恢復后又會變成從服務器
### 配置副本集
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() 命令
啟動服務器后,我們還不知道我們的小伙伴具體是誰,要配置一下(隨便鏈接一個服務器)


>**注意:** 不是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/)