《MongoDB实战》读书笔记-索引、复制和分片

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

索引与查询优化

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

1. 索引理论

复合索引和分离索引

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

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

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

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

索引效率

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

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

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

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

B 树

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

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

006y8mN6gy1g7u32bzv1kj30zg0n74bf.jpg

参考链接:

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 个节点组成:主节点、从节点、仲裁节点。主从节点是一等的;仲裁节点不复制数据,中立观察。

006y8mN6gy1g7uawc9w17j30mk0j0qbw.jpg

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

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

3. 主从复制

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

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

4. 写关注和读拓展

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

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

分片

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

006y8mN6gy1g7ubh0y9azj30r20l6q8y.jpg

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

最后

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

完!