这是阅读《MongoDB 实战》所做的,关于索引、复制和分片方面的读书笔记。
索引与查询优化
查询是非常高频的操作,大数据、高频读的场景下,查询的效率会是性能的瓶颈。设置合适的索引,可以充分利用数据结构(B 数)和物理硬件的优势。
1. 索引理论
复合索引和分离索引
一个查询中,要是有多个字段,比如 2 个字段。分离索引是:查找每个索引的匹配集合,取得这些匹配集合的交集。复合索引是:逐步根据索引的顺序做查询。
比如有一个食谱,我们根据种类和菜名来做索引:
1 | 肉类 |
⚠️:复合索引中的顺序是非常重要的,如果设置的索引不合适,那么就相当于现行扫描文档。抽象来说,如果有一个针对 a-b 的复合索引,那么仅针对 a 的索引就是冗余的。比如例子中,仅针对种类的索引就是冗余的,但是种类索引可以降低扫描时间(和 Btree 有关)。
索引效率
正确的索引,也不一定会有快速的查询:索引和数据集无法全部放入内存。
如果内存充足,所有使用的数据文件都会载入内存,对应内存发生变化时(比如写操作),结果会异步刷到磁盘上。
如果内存不足,就无法全部装入内存,出现页错误,操作系统会频繁访问磁盘读取需要数据。数据集过大时候,任何写操作都要去磁盘,会出现颠簸情况,性能下滑。
因此,应该首先保证索引都能装入内存,复合索引时,尽量减少键的数量。
B 树
在 Mongo Version2 中,B 树仅用于索引。集合存储是双向列表。
对于复合索引的底层结构,以下面为例,是根据姓、名和生日来建立的复合索引。如果要查询(Akroyd, Kirsten, 1978-11-02)的数据,那么会先按照顺序查找,根据第一个索引,找到了只有左侧两个复合要求;再在左侧两个集合中查找第二个索引;直到找到符合要求的数据为止。
参考链接:
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 个节点组成:主节点、从节点、仲裁节点。主从节点是一等的;仲裁节点不复制数据,中立观察。
副本集基于两个机制:oplog 和心跳。oplog 是记录数据的变更;心跳是检测主节点是否有效。
在副本集中,「提交」是指:数据变动都被复制到从节点。否则就是未提交。
3. 主从复制
不推荐,副本集才是正道,原因如下:
- 故障转移手动操作(没有仲裁节点)
- oplog 近存在主节点,恢复苦难
4. 写关注和读拓展
写关注:设置writeConcern
参数,通过属性设置来指定 wtimeout、w。
读拓展:单台服务器无法承受程序的读负载,将查询分配到副本上。
分片
这是一个有意思的概念,尤其是「复制」对比的时候。复制是指数据都保存在单机上,向其他副本迁移,理论上所有主从节点数据是一致的;分片是指由于空间有限,单机承受不了数据量,同一个数据库分布在不同的数据库上,这些数据库形成了一个宏观意义上的节点。
值得称赞的是,mongo 提供分片机制,无需变动代码。
最后
作为中前台开发,开发中几乎接触不到复制、分片和部署的逻辑。专业事还得交给专业的人来做,毕竟每个人精力有限,不能面面俱到。但了解复制和分片的原理,有助于加深对 mongo 的理解,也可能会在以后做架构的时候发挥作用。
完!