我对MySQL索引的工作原理非常感兴趣,更具体地说,它们如何在不扫描整个表的情况下返回所请求的数据?
我知道这离题了,但如果有人能给我详细解释一下,我会非常非常感谢。
我对MySQL索引的工作原理非常感兴趣,更具体地说,它们如何在不扫描整个表的情况下返回所请求的数据?
我知道这离题了,但如果有人能给我详细解释一下,我会非常非常感谢。
当前回答
我想发表我的意见。我还远不是数据库专家,但我最近读了一些关于这个主题的文章;足以让我试着申请ELI5。所以,这是一个外行的解释。
我的理解是,索引就像你的表的迷你镜子,很像一个关联数组。如果你给它一个匹配的键,那么你可以在一个“命令”中跳转到那一行。
但是如果没有索引/数组,查询解释器必须使用for循环遍历所有行并检查是否匹配(全表扫描)。
拥有索引的“缺点”是额外的存储空间(用于迷你镜像),而“优点”是更快地查找内容。
请注意(依赖于您的db引擎)创建主键、外键或唯一键会自动设置各自的索引。同样的原理基本上就是这些钥匙为什么以及如何工作。
其他回答
您必须知道的第一件事是,索引是一种避免扫描整个表以获得您正在寻找的结果的方法。
有不同种类的索引,它们是在存储层实现的,所以它们之间没有标准,它们也取决于你使用的存储引擎。
InnoDB和B+树索引
对于InnoDB,最常见的索引类型是基于B+树的索引,它以排序的顺序存储元素。此外,您不必访问实际表来获取索引值,这使得您的查询返回速度更快。
关于这种索引类型的“问题”是,您必须查询最左边的值才能使用索引。因此,如果您的索引有两个列,例如last_name和first_name,那么查询这些字段的顺序非常重要。
给定下面的表格:
CREATE TABLE person (
last_name VARCHAR(50) NOT NULL,
first_name VARCHAR(50) NOT NULL,
INDEX (last_name, first_name)
);
这个查询将利用索引:
SELECT last_name, first_name FROM person
WHERE last_name = "John" AND first_name LIKE "J%"
但接下来的一个则不然
SELECT last_name, first_name FROM person WHERE first_name = "Constantine"
因为首先查询的是first_name列而不是索引中最左边的列。
最后一个例子更糟糕:
SELECT last_name, first_name FROM person WHERE first_name LIKE "%Constantine"
因为现在,你比较的是索引中最右边字段的最右边部分。
哈希索引
这是一种不同的索引类型,不幸的是,只有内存后端支持。它速度极快,但仅对完全查找有用,这意味着您不能将其用于>、<或like等操作。
因为它只适用于内存后端,所以您可能不会经常使用它。我现在能想到的主要情况是,您在内存中创建一个临时表,其中包含来自另一个选择的一组结果,并使用散列索引在这个临时表中执行许多其他选择。
如果您有一个大的VARCHAR字段,您可以在使用B-Tree时“模拟”使用哈希索引,方法是创建另一列并在其中保存大值的哈希。假设您要在字段中存储一个url,值相当大。您还可以创建一个名为url_hash的整数字段,并在插入url时使用像CRC32这样的哈希函数或任何其他哈希函数来哈希。然后,当你需要查询这个值时,你可以这样做:
SELECT url FROM url_table WHERE url_hash=CRC32("http://gnu.org");
上述示例的问题在于,由于CRC32函数生成了一个非常小的散列,因此最终会在散列值中产生大量冲突。如果你需要精确的值,你可以通过以下方法修复这个问题:
SELECT url FROM url_table
WHERE url_hash=CRC32("http://gnu.org") AND url="http://gnu.org";
即使碰撞数很高,哈希仍然是值得的,因为你只会对重复的哈希执行第二次比较(字符串一)。
不幸的是,使用这种技术,您仍然需要点击表来比较url字段。
总结
每当你想谈论优化时,你可能会考虑一些事实:
整数比较比字符串比较快得多。以InnoDB中哈希索引仿真为例进行了说明。 也许,在流程中添加额外的步骤会使其更快,而不是更慢。可以通过以下事实来说明这一点:优化SELECT可以分为两个步骤,第一个步骤将值存储在新创建的内存表中,然后在第二个表上执行较重的查询。
MySQL也有其他索引,但我认为B+树索引是最常用的,哈希索引是一个很好的东西,但你可以在MySQL文档中找到其他索引。
我强烈推荐你去读“高性能MySQL”这本书,上面的答案肯定是基于它关于索引的章节。
在答案列表中添加一些可视化表示。
MySQL使用了一个额外的间接层:次要索引记录指向主索引记录,主索引本身保存磁盘上的行位置。如果行偏移量发生变化,则只需要更新主索引。
注意:磁盘数据结构在图中看起来是平面的,但实际上是一个 B +树。
来源:链接
基本上表上的索引就像书中的索引一样(这就是这个名字的由来):
Let's say you have a book about databases and you want to find some information about, say, storage. Without an index (assuming no other aid, such as a table of contents) you'd have to go through the pages one by one, until you found the topic (that's a full table scan). On the other hand, an index has a list of keywords, so you'd consult the index and see that storage is mentioned on pages 113-120,231 and 354. Then you could flip to those pages directly, without searching (that's a search with an index, somewhat faster).
当然,索引的有用程度取决于许多事情——举几个例子,使用上面的明喻:
if you had a book on databases and indexed the word "database", you'd see that it's mentioned on pages 1-59,61-290, and 292 to 400. In such case, the index is not much help and it might be faster to go through the pages one by one (in a database, this is "poor selectivity"). For a 10-page book, it makes no sense to make an index, as you may end up with a 10-page book prefixed by a 5-page index, which is just silly - just scan the 10 pages and be done with it. The index also needs to be useful - there's generally no point to index e.g. the frequency of the letter "L" per page.
索引用于快速查找具有特定列值的行。如果没有索引,MySQL必须从第一行开始,然后通读整个表以找到相关的行。桌子越大,花费就越多。如果表中存在相关列的索引,MySQL可以快速确定要在数据文件中间查找的位置,而不必查看所有数据。这比按顺序读取每一行快得多。
索引添加一个包含搜索条件列和指针的数据结构 对象所在行的内存磁盘地址 其他信息 对索引数据结构进行排序,优化查询效率 该查询查找索引中的特定行;索引指向将查找剩余信息的指针。 索引将查询必须搜索的行数从17减少到4。
我想发表我的意见。我还远不是数据库专家,但我最近读了一些关于这个主题的文章;足以让我试着申请ELI5。所以,这是一个外行的解释。
我的理解是,索引就像你的表的迷你镜子,很像一个关联数组。如果你给它一个匹配的键,那么你可以在一个“命令”中跳转到那一行。
但是如果没有索引/数组,查询解释器必须使用for循环遍历所有行并检查是否匹配(全表扫描)。
拥有索引的“缺点”是额外的存储空间(用于迷你镜像),而“优点”是更快地查找内容。
请注意(依赖于您的db引擎)创建主键、外键或唯一键会自动设置各自的索引。同样的原理基本上就是这些钥匙为什么以及如何工作。