数据库索引背后的基本概念
创建索引没有统一的通用公式,但最有用的方法取决于一些常见的想法。这些共同思想的构建块驻留在散列函数、 B 树和 B + 树数据结构中。
哈希函数是一个定义良好的数学函数,用于将大型(通常是可变大小和复杂的)数据值转换为单个整数或一组字节。哈希函数的输出可以用不同的名称来表示,包括哈希代码、哈希值、哈希和和校验和。散列码通常用作关联数组的键,也称为散列映射。当您将复杂的数据库属性值映射到用于创建索引的哈希代码时,哈希函数非常方便。
树型数据结构以树型结构分布一组值。值是以分层的方式结构化的,在树中的某些节点之间具有链接或指针。二叉树是最多有两个子节点的树: 一个在左边,另一个在右边。一个节点可以是父节点,在这种情况下它最多有两个节点,或者它可以是一个叶子,在这种情况下它是链中的最后一个节点。参见下图了解二叉树数据结构。
B 树是二叉树的推广。它允许一个父节点有两个以上的子节点。B 树保持数据排序,因此允许有效的搜索和数据访问。B + 树是 B 树的一种特殊变体。在 B + 树中,所有的记录都存储在叶子中,叶子按顺序链接。B +-tree 是用于存储数据库索引的最常见的树结构。
尽管基本构建块是相同的,但是在不同的 NoSQL 产品中创建和应用索引的方式是不同的
MongoDB的索引与排序
MongoDB 围绕索引集合提供了大量丰富的选项,以提高查询性能。默认情况下,它为其包含的所有集合在 _ id 属性上创建一个索引。
为了理解索引的重要性和影响,您还需要一些工具来度量使用和不使用索引的查询性能。
在 MongoDB 中,通过内置的工具来解释查询计划和识别运行缓慢的查询,可以方便地度量查询性能。
查询计划描述数据库服务器运行给定查询所必须执行的操作。首先,在深入研究explain实用程序的输出及其传达的内容之前,运行该实用程序。要获取评级集合中的所有项目,您可以这样查询:
db.ratings.find();
要为这个查询运行explain,您可以运行这个查询:
db.ratings.find ()
explain的结果如下:
{
"cursor" : "BasicCursor",
"nscanned": 1000209,
"nscannedobjects" : 1000209, "n":1000209,
"millis":1549,
"indexBounds":{
}
}
输出结果显示,返回1,000,209(超过100万)个文档需要1,549毫秒。在查询这1,000,209份文件时,检查了1,000,209个项目。它还声明使用了 BasicCursor。
很明显,解释函数的输出也是一个文档。如上例所示,其属性如下:
-
Cursor-用于返回查询结果集的游标。游标可以有两种类型: 基本游标和 B 树游标。基本游标意味着表扫描和 B 树游标意味着索引被使用。
-
nscanned - 当使用索引时,它对应于索引条目的数量。
-
nscannedobjects - 文件扫描的数量。
-
n-返回的文档数量。
-
Milis-运行查询所花费的时间(毫秒)。
-
IndexBound-表示与查询匹配的最小和最大索引键。只有在使用索引时,此字段才相关。
下一个示例查询评级的子集。评级收集包括一组用户对一组电影的排名(从1到5)。若要筛选评级集合,请将其限制为与特定电影相关的子集。评分集合只有电影 ID,因此要将 ID 与名称关联,需要在电影集合中查找值。
我使用玩具总动员电影作为一个例子。
要获得与 ToyStory 相关的文档,您可以利用一个很好的旧正则表达式。
所有与电影集中的玩具总动员相关的文档都可以这样查询:
db.movies.find({title:/Toy Story/i});
运行结果 如下:
{ "_id" : 1, "title" : "Toy Story (1995)", "genres" : [ "Animation", "children's","Comedy"]}
{ "_id" : 3114, "title" : "Toy Story 2 (1999)", "genres" :["Animation", "Children's","Comedy"]}
接下来,获取“玩具总动员”的电影 ID (恰好是1) ,并使用该 ID 查找所有用户的所有相关评级。但是,在此之前,请运行explain函数来查看数据库如何运行正则表达式查询以在电影集合中查找 Toy Story。你可以这样运行explain:
db.movies.find({title: /Toy Story/i}).explain();
输出如下:
{"cursor" : "BasicCursor",
"nscanned":3883,
"nscannedobjects" : 3883,
"n":2,
"millis" :6,
"indexBounds":{
}
}
在电影集合上运行一个 count,使用 db.animes.count () ; 来验证文档的数量,您会发现它与查询解释的 n 扫描值和 n 扫描对象值相匹配。这意味着正则表达式查询导致了表扫描,这是不高效的。文档数量被限制在3,883个,因此查询仍然足够快,只需要6毫秒。
要列出所有与《玩具总动员》(更准确地说是《玩具总动员》(1995))相关的评级,你可以查询如下:
db.ratings.find({movie_id:1})
要查看前一个查询的查询计划,请按如下方式进行:
db.ratings.find({movie_id: 1}).explain();
产出应如下:
{"cursor" : "BasicCursor", "nscanned" : 1000209,
"nscannedobjects" : 1000209, "n":2077,
"millis":484,
"indexBounds":
{
}
}
在这个阶段,很明显查询没有以最佳方式运行,因为 n 扫描和 n 扫描对象计数读取1,000,209,这是集合中的所有文档。这是引入索引和进行优化的好时机。
本文正在参加「金石计划 . 瓜分6万现金大奖」