当前位置: 首页 > 科技观察

深入理解有效的MongoDB索引

时间:2023-03-18 15:49:08 科技观察

索引是一个具有自己独特存储的对象,它提供了对集合的快速访问路径。索引的存在主要是为了提高性能,因此在优化MongoDB性能时理解和有效使用索引非常重要。1、B树索引B树索引是MongoDB默认的索引结构。以下是B树索引结构的高级概述。B树索引具有分层树结构。树的顶部是标题块。该块包含指向任何给定键值范围的适当分支块的指针。对于更具体的范围,分支块通常会指向适当的叶块,或者对于更大的索引,另一个分支块。叶块包含一个键值列表和一个指向磁盘上文档位置的指针。看上图,我们想象一下MongoDB是如何遍历这个索引的。如果我们需要访问“BAKER”的记录,我们将首先查阅标题块。头块会告诉我们从A到K的键存储在最左边的分支块中。访问这个分支块,我们发现从A到D开始的键值存储在最左边的叶子块中。查询此叶块,我们找到值“BAKER”及其关联的磁盘位置,我们将使用它来获取相关文件。叶块包含指向先前和后续叶块的链接。这允许我们按升序或降序扫描索引,并允许使用索引处理使用$gt或$lt运算符的范围查询。B树索引与其他索引策略相比具有以下优点:由于每个叶节点都处于相同的深度,因此性能非常可预测。从理论上讲,集合中的任何文档都不会占用超过三个或四个I/O。B树为大型集合提供了良好的性能,因为深度最多为四个(一个头块、两个分支块级别和一个叶块级别)。一般来说,没有文件需要超过四个I/O来定位。在实践中,由于头块几乎总是已经加载到内存中,而分支块通常已经加载到内存中,因此实际读取物理磁盘的次数通常只有一两次。B树索引支持范围查询,并且由于指向前一个和下一个叶块的链接,因此可以进行精确查找。B树索引提供了灵活高效的查询性能。但是,在数据更改时维护B树可能很昂贵。例如,考虑在上图中插入一个键值为“NIVEN”的文档。要插入到集合中,我们必须在“L-O”块中添加一个新条目。如果该区域有可用空间,则成本很高,但可能不会过高。但是如果块中没有空闲空间会发生什么?如果叶块中没有用于新条目的可用空间,则需要进行索引拆分。必须分配一个新块,并将现有块中的一半条目移动到新块中。除此之外,还需要在分支块中添加一个新条目(以指向新创建的叶块)。如果分支块中没有可用空间,则分支块也必须被拆分。这些索引拆分是一项昂贵的操作:必须分配新块并将索引条目从一个块移动到另一个块。2.索引选择性一个属性或一组属性的选择性是对这些属性建立索引的有用性的常用度量。如果文档或索引具有大量唯一值或很少重复值,则它们是选择性的。例如,DATE_AND_TIME_OF_BIRTH属性会非常有选择性,而GENDER属性则不会被选中。选择性索引比非选择性索引更有效,因为它们更直接地指向特定值。MongoDB优化器通常会使用最具选择性的索引。3.唯一索引唯一索引是防止组成索引的属性有任何重复值的索引。如果您尝试在包含此类重复值的集合上创建唯一索引,则会出现错误。同样,如果您尝试插入包含重复的唯一索引键值的文档,您将得到一个错误。通常创建唯一索引是为了防止重复值,而不是为了提高性能。然而,唯一索引文件通常非常高效——它们只能指向一个文件,因此非常有选择性。4.连接索引连接索引只是一个包含多个属性的索引。连接键的优点是它比单个键索引更具选择性。与由单个属性组成的索引相比,属性的组合将指向更少数量的文档。包含find()或$match子句中引用的所有属性的连接索引将特别有效。如果您经常查询集合中的多个文档,最好为这些文档创建一个连接索引。例如,我们可以通过Surname(姓氏)和Firstname(名字)来查询人物集合。在这种情况下,我们可能希望在Surname和Firstname上创建一个索引。例如:db.people.createIndex({"Surname":1,"Firstname":1});使用这样的索引,我们可以快速找到people中所有匹配的Surname\Firstname组合。这样的索引比单独的姓氏索引或单独的姓氏和名字索引更有效。如果连接索引只能在所有键都出现find()或$match时使用,那么它的用途可能非常有限。幸运的是,可以非常有效地使用连接索引,提供任何初始或主要属性。主要属性是在索引定义中最早指定的属性。上图显示了将属性添加到连接索引时获得的改进。涉及的查询是关于1,000,000份文档的人员集合:db.people.find({Firstname:"KAREN",Surname:"SMITH",dob:ISODate("2006-01-21T05:55:32.520Z")},{_id:0,电话:1});例如,我们通过提供姓氏、名字和出生日期来检索电话号码。完整的集合扫描需要我们访问所有1,000,000个文档。仅索引姓氏就将其减少到20,028个文件-实际上是集合中的所有“SMITHS”。添加firstname将文档数量减少到188。通过添加dob,我们只能访问两个:访问索引条目并从那里访问集合以获取电话号码。最新的优化是增加电话号码tel属性。现在我们根本不需要访问集合——我们只需要索引。这有时称为“覆盖率”指数。请注意,覆盖索引通常要求查询包括一个投影,该投影消除除索引中包含的属性之外的所有属性。5.连接索引指南以下指南将帮助确定何时使用连接索引,以及如何确定应包括哪些属性以及以何种顺序包括在内。为与find()或$match条件一起出现的属性在集合中创建连接索引。如果属性有时显示为find()或$match,请将它们放在索引的开头。如果连接索引还支持未指定所有属性的查询,则连接索引更有用。例如,createIndex({"Surname":1,"Firstname":1})比createIndex({"Firstname":1,"Surname":1})更有用,因为只查询姓氏比查询更好因为只有名字更有可能发生。属性越有选择性,它在索引前端就越有用。但是请注意,WiredTiger索引压缩可以从根本上缩小索引。当领先列的选择较少时,索引压缩更有效。所以如果属性的顺序不是由前面三个考虑决定的,你可能要尝试索引顺序。综上所述,在MongoDB的优化过程中,只有深刻理解内部的索引机制,才能更好的提升MongoDB的性能。