这是阅读《MongoDB实战》所做的,关于索引、复制和分片方面的读书笔记。

# 索引与查询优化

查询是非常高频的操作,大数据、高频读的场景下,查询的效率会是性能的瓶颈。设置合适的索引,可以充分利用数据结构(B数)和物理硬件的优势。

# 1. 索引理论

# 复合索引和分离索引

一个查询中,要是有多个字段,比如2个字段。分离索引是:查找每个索引的匹配集合,取得这些匹配集合的交集。复合索引是:逐步根据索引的顺序做查询。

比如有一个食谱,我们根据种类和菜名来做索引:

肉类
	- 辣子鸡:第12页
	- 鱼肉:第139页
1
2
3

⚠️:复合索引中的顺序是非常重要的,如果设置的索引不合适,那么就相当于现行扫描文档。抽象来说,如果有一个针对a-b的复合索引,那么仅针对a的索引就是冗余的。比如例子中,仅针对种类的索引就是冗余的,但是种类索引可以降低扫描时间(和Btree有关)。

# 索引效率

正确的索引,也不一定会有快速的查询:索引和数据集无法全部放入内存

如果内存充足,所有使用的数据文件都会载入内存,对应内存发生变化时(比如写操作),结果会异步刷到磁盘上。

如果内存不足,就无法全部装入内存,出现页错误,操作系统会频繁访问磁盘读取需要数据。数据集过大时候,任何写操作都要去磁盘,会出现颠簸情况,性能下滑。

因此,应该首先保证索引都能装入内存,复合索引时,尽量减少键的数量。

# B树

在Mongo Version2中,B树仅用于索引。集合存储是双向列表。

对于复合索引的底层结构,以下面为例,是根据姓、名和生日来建立的复合索引。如果要查询(Akroyd, Kirsten, 1978-11-02)的数据,那么会先按照顺序查找,根据第一个索引,找到了只有左侧两个复合要求;再在左侧两个集合中查找第二个索引;直到找到符合要求的数据为止。

image-20191011110751982

参考链接:

# 2. 索引实践

# 索引类型

根据索引设置时的属性的不同,常见的有:唯一性索引、稀疏索引和多键索引。

唯一性索引

说明:被设置为索引的字段,不能重复出现,否则会报错。

创建方式:db.col.createIndex({name: 1}, {unique: true})

⚠️:适用于插入数据前先创建索引的情况

# 稀疏索引

说明:索引默认是密集型的,是指为集合中每个文档都建立索引。例如前面的例子,即使文档没有name字段,那么查询索引时候,没有name字段的文档匹配null即可。

创建方式:db.col.createIndex({name: 1}, {sparse: true})

优点:

  • 占用较少的空间
  • 适用于不是为所有文档增加唯一性索引
  • 适用于历史遗留的文档,无法保证字段存在

# 多键索引

说明:在数组字段上建立索引。mongo中,多键索引是默认开启的。

原理:数组中每个元素,都指向文档。

# 3. 查询管理

# 构建索引

分为为索引值排序、排序值插入索引中,并且会占用写锁,其他程序无法读写数据库。

在迁移历史数据和索引的时候,先迁移数据再构建集合,比线构建集合再迁移数据 的做法更优秀。

# 后台索引

设置background为true。虽然仍会占用写锁,但会停下来,让其他操作读写操作访问数据库。适合在流量最低的时候,完成索引构建。

# 备份

mongodump和mongorestore只能保存集合和索引说明。

如果想备份索引,必须直接备份mongo的数据文件。

# 压紧删除

对于删除大量数据,可能造成索引碎片化。解决方法是重建索引或者执行db.col.reIndex().

应该在子节点执行此命令,再进行节点替换,因为它会占用写锁,造成无法读写操作。

# 4. 查询优化

两个比较重要的原理,一个是覆盖索引,一个复合键的顺序

覆盖索引是指:查询的关键字和索引完全一致。

复合键的顺序遵循:搜索成本由低到高的原则排列。

# 复制

# 1. 复制概述

定义:在多台服务器上分布并管理数据库服务器。有2种复制风格:主从复制和副本集(生产环境推荐)。

复制的作用是冗余,因为复制是异步的,因此任何节点的延迟都不会影响主节点性能。

副本不是备份替代品:备份是某事刻的快照;副本是最新的。

作用:故障转移、均衡读负载。

# 2. 副本集

最小的副本集由3个节点组成:主节点、从节点、仲裁节点。主从节点是一等的;仲裁节点不复制数据,中立观察。

image-20191011153854119

副本集基于两个机制:oplog和心跳。oplog是记录数据的变更;心跳是检测主节点是否有效。

在副本集中,「提交」是指:数据变动都被复制到从节点。否则就是未提交。

# 3. 主从复制

不推荐,副本集才是正道,原因如下:

  • 故障转移手动操作(没有仲裁节点)
  • oplog近存在主节点,恢复苦难

# 4. 写关注和读拓展

写关注:设置writeConcern参数,通过属性设置来指定wtimeout、w。

读拓展:单台服务器无法承受程序的读负载,将查询分配到副本上。

# 分片

这是一个有意思的概念,尤其是「复制」对比的时候。复制是指数据都保存在单机上,向其他副本迁移,理论上所有主从节点数据是一致的;分片是指由于空间有限,单机承受不了数据量,同一个数据库分布在不同的数据库上,这些数据库形成了一个宏观意义上的节点。

image-20191011155847647

值得称赞的是,mongo提供分片机制,无需变动代码。

# 最后

作为中前台开发,开发中几乎接触不到复制、分片和部署的逻辑。专业事还得交给专业的人来做,毕竟每个人精力有限,不能面面俱到。但了解复制和分片的原理,有助于加深对mongo的理解,也可能会在以后做架构的时候发挥作用。

完!