MySQL 指数澄清的表现
MySQL performance with index clairification
假设我有一个 mysql table,在列 'name':
上有一个索引
我做这个查询:
select * from name_table where name = 'John';
假设从具有 100 行的 table 返回了 5 个结果。
假设我现在插入 100 万个新行,其中没有一个名为 John,所以 table 中仍然只有 5 个 John。 select 语句是否会像以前一样快,那么插入所有这些行是否会对索引 table 的读取速度产生影响?
索引有它们自己的 "tables",当 MySQL 引擎确定查找引用索引列时,查找发生在这个 table 上。它本身并不是真正的 table,但要点已验证。
也就是说,它会慢几纳秒,但这不是您应该关心的事情。
更重要的是,关注相关数据的索引,column order,因为这些对数据库性能的影响 MUCH。
要了解更多关于幕后发生的事情,请查询 EXPLAIN
:
EXPLAIN select * from name_table where name = 'John';
注意:除了 link 中列出的列顺序之外,在 之后具有可变长度列 (VARCHAR
) 是一个好(不,很棒)的想法 它们的固定长度对应项 (CHAR
),因为在查找期间,引擎必须查看行、读取列长度,然后向前跳过查找(请注意,这是仅适用于非索引列),或者阅读 table 声明并知道它总是必须查看具有偏移量 X 的列。它在幕后更复杂,但如果你可以移动所有固定长度的列到了前面,你会感谢自己。基本上:
Indexed columns.
Everything Fixed-Length in order according to the link.
Everything Variable-Length in order according to the link.
是的,一样快。
(除了 Mike 的回答中的精彩观点之外...)关于索引(尤其是 B 树索引),我们应该提出一个重要的观点:
索引中的条目存储"in order"。
索引的组织方式允许数据库非常快速地识别索引中包含它正在查找的条目的块(或者如果没有匹配的条目,则包含条目的块。)
这意味着数据库不需要查看索引中的每个条目。给定一个像您问题中的谓词:
WHERE name = 'John'
使用前导列为 name
的索引,数据库可以消除大量不需要检查的块。
索引开头附近的块包含 'Adrian'
到 'Anna'
的条目,在索引稍晚的位置,块包含 Caleb
到 Carl
的条目,在索引 James
到 Jane
中更远
由于索引的组织方式,数据库实际上 "knows" 我们要查找的条目不能在任何这些块中(因为索引是有序的,所以值不可能John
可能会出现在我们提到的那些块中)。因此需要检查这些块中的 none。 (数据库在极少数操作中计算出,索引中 98% 的块可以从考虑中消除。
高基数 = 性能好
由此得出的结论是,索引对具有高基数的列最有效。也就是说,该列中有大量不同的值,并且这些值是唯一的或几乎唯一的。
这应该清楚了您所问问题的答案。您可以将 brazilians 行添加到 table。如果这些行中只有五行的值为
John
在名称列中,当您进行查询时
WHERE name = `John`
它会一样快。当您在 table.
中有一千行时,数据库将能够尽可能快地找到您要查找的条目
(随着索引变大,它确实将 "levels" 添加到索引中,以向下遍历到叶节点...因此,由于多了一些操作,它变得越来越慢。性能真正开始下降的地方是 InnoDB 缓冲区缓存太小,我们不得不等待(相比之下非常慢)磁盘 io 操作将块提取到内存中。
基数低 = 性能差
基数低的列上的索引效率低得多。例如,具有两个可能值的列,值在 table 中的行之间均匀分布(大约一半的行具有一个值,另一半具有另一个值。)在这种情况下,数据库无法消除 98% 的块,或 90% 的块。数据库必须通过索引中一半的块,然后(通常)对底层 table 中的页面执行查找以获取该行的其他值。
但是,如果有数以亿计的行,其中有一列 gender
,有两个值 'M'
和 'F'
,以 gender
作为前导列的索引将 不有效地满足查询
WHERE gender = 'M'
... 因为我们有效地告诉数据库检索 table 中的一半行,并且这些行很可能会均匀分布在 table 中。因此几乎 table 中的每一页都将包含我们需要的至少一行,数据库将选择进行完整的 table 扫描(以查看 table 中每个块中的每一行=75=]) 来定位行,而不是使用索引。
因此,就使用索引在 table 中查找行的性能而言... table 的大小并不是真正的问题。真正的问题是索引中值的基数,我们正在寻找多少个不同的值,以及需要返回多少行。
假设我有一个 mysql table,在列 'name':
上有一个索引我做这个查询:
select * from name_table where name = 'John';
假设从具有 100 行的 table 返回了 5 个结果。
假设我现在插入 100 万个新行,其中没有一个名为 John,所以 table 中仍然只有 5 个 John。 select 语句是否会像以前一样快,那么插入所有这些行是否会对索引 table 的读取速度产生影响?
索引有它们自己的 "tables",当 MySQL 引擎确定查找引用索引列时,查找发生在这个 table 上。它本身并不是真正的 table,但要点已验证。
也就是说,它会慢几纳秒,但这不是您应该关心的事情。
更重要的是,关注相关数据的索引,column order,因为这些对数据库性能的影响 MUCH。
要了解更多关于幕后发生的事情,请查询 EXPLAIN
:
EXPLAIN select * from name_table where name = 'John';
注意:除了 link 中列出的列顺序之外,在 之后具有可变长度列 (VARCHAR
) 是一个好(不,很棒)的想法 它们的固定长度对应项 (CHAR
),因为在查找期间,引擎必须查看行、读取列长度,然后向前跳过查找(请注意,这是仅适用于非索引列),或者阅读 table 声明并知道它总是必须查看具有偏移量 X 的列。它在幕后更复杂,但如果你可以移动所有固定长度的列到了前面,你会感谢自己。基本上:
Indexed columns.
Everything Fixed-Length in order according to the link.
Everything Variable-Length in order according to the link.
是的,一样快。
(除了 Mike 的回答中的精彩观点之外...)关于索引(尤其是 B 树索引),我们应该提出一个重要的观点:
索引中的条目存储"in order"。
索引的组织方式允许数据库非常快速地识别索引中包含它正在查找的条目的块(或者如果没有匹配的条目,则包含条目的块。)
这意味着数据库不需要查看索引中的每个条目。给定一个像您问题中的谓词:
WHERE name = 'John'
使用前导列为 name
的索引,数据库可以消除大量不需要检查的块。
索引开头附近的块包含 'Adrian'
到 'Anna'
的条目,在索引稍晚的位置,块包含 Caleb
到 Carl
的条目,在索引 James
到 Jane
中更远
由于索引的组织方式,数据库实际上 "knows" 我们要查找的条目不能在任何这些块中(因为索引是有序的,所以值不可能John
可能会出现在我们提到的那些块中)。因此需要检查这些块中的 none。 (数据库在极少数操作中计算出,索引中 98% 的块可以从考虑中消除。
高基数 = 性能好
由此得出的结论是,索引对具有高基数的列最有效。也就是说,该列中有大量不同的值,并且这些值是唯一的或几乎唯一的。
这应该清楚了您所问问题的答案。您可以将 brazilians 行添加到 table。如果这些行中只有五行的值为
John
在名称列中,当您进行查询时
WHERE name = `John`
它会一样快。当您在 table.
中有一千行时,数据库将能够尽可能快地找到您要查找的条目(随着索引变大,它确实将 "levels" 添加到索引中,以向下遍历到叶节点...因此,由于多了一些操作,它变得越来越慢。性能真正开始下降的地方是 InnoDB 缓冲区缓存太小,我们不得不等待(相比之下非常慢)磁盘 io 操作将块提取到内存中。
基数低 = 性能差
基数低的列上的索引效率低得多。例如,具有两个可能值的列,值在 table 中的行之间均匀分布(大约一半的行具有一个值,另一半具有另一个值。)在这种情况下,数据库无法消除 98% 的块,或 90% 的块。数据库必须通过索引中一半的块,然后(通常)对底层 table 中的页面执行查找以获取该行的其他值。
但是,如果有数以亿计的行,其中有一列 gender
,有两个值 'M'
和 'F'
,以 gender
作为前导列的索引将 不有效地满足查询
WHERE gender = 'M'
... 因为我们有效地告诉数据库检索 table 中的一半行,并且这些行很可能会均匀分布在 table 中。因此几乎 table 中的每一页都将包含我们需要的至少一行,数据库将选择进行完整的 table 扫描(以查看 table 中每个块中的每一行=75=]) 来定位行,而不是使用索引。
因此,就使用索引在 table 中查找行的性能而言... table 的大小并不是真正的问题。真正的问题是索引中值的基数,我们正在寻找多少个不同的值,以及需要返回多少行。