繁体   English   中英

何时在SQL表字段(MySQL)上添加索引?

[英]When to add an index on a SQL table field (MySQL)?

我被告知如果你知道你经常使用一个字段进行连接,那么在它上面创建一个索引可能会很好。

我通常理解索引表的概念(很像纸质书中的索引允许您查找特定术语而无需逐页搜索)。 但我不太清楚何时使用它们。

假设我有3个表:USERS,COMMENTS和VOTES表。 我想创建一个类似Stackoverflow的评论线程,其中查询返回注释以及这些注释上/下投票的数量。

USERS table
user_id user_name   
 1         tim
 2         sue
 3         bill 
 4         karen
 5         ed

COMMENTS table
comment_id topic_id    comment   commenter_id
 1            1       good job!         1
 2            2       nice work         2
 3            1       bad job :)        3

VOTES table
 vote_id    vote  comment_id  voter_id
  1          -1       1          5
  2           1       1          4
  3           1       3          1
  4          -1       2          5
  5           1       2          4

这是在topic_id = 1上返回投票的查询和SQLFiddle

select u.user_id, u.user_name,
   c.comment_id, c.topic_id, c.comment,
   count(v.vote) as totals, sum(v.vote > 0) as yes, sum(v.vote < 0) as no,
   my_votes.vote as did_i_vote
from comments c
join users u on u.user_id = c.commenter_id
left join votes v on v.comment_id = c.comment_id
left join votes my_votes on my_votes.comment_id = c.comment_id
and my_votes.voter_id = 1
where c.topic_id = 1
group by c.comment_id, u.user_name, c.comment_id, c.topic_id, did_i_vote;

让我们假设评论和投票的数量达到数百万。 为了加快查询速度,我的问题是我应该在comments.commenter_idvotes.voter_idvotes.comment_idvotes.voter_id votes.comment_id吗?

在SQL表中使用索引的位置并不总是很明确。 但是有一些一般的经验法则可以帮助您在大多数情况下做出决定。

  1. 将索引放在where子句中使用的列上
  2. 将索引放在您用于加入的列上。
  3. 尽量不要在同一个表中的列上使用超过4-5个索引。

您应该记住的一般概念是:

  1. 您使用的任何索引都会更快地搜索这些列。
  2. 您添加的任何索引导致插入此表的速度要慢一些。
  3. 从前两个。 您有责任决定在表上执行多少次插入和查询,以决定是否使用索引以及是否使用哪些列。

编辑

@AndrewLazarus评论非常重要,我决定将其添加到答案中:

  1. 不要在只有很少不同值的列上使用索引。 例如,当只有很少的状态时,保持状态的列或布尔值。 不这样做的原因是索引并没有真正帮助你,因为它只会被值的数量除以,因为你只有少数几个,所以没有任何实际的好处。 索引会占用更多空间,插入时预制会变慢,但查询时不会获得明显更好的性能

这是使用http://www.sqlfiddle.com/#!2/94daa/1使用的一些密钥的更新

引擎必须将使用索引的成本与不这样做的成本进行比较。 您会注意到我必须添加更多行才能获得所使用的索引。

使用索引,引擎必须使用索引来获取匹配值,这很快。 然后它必须使用匹配来查找表中的实际行。 如果索引没有缩小行数,那么只需查找表中的所有行就可以更快。

我不确定mysql是否有类似于SQL Server聚簇索引的东西。 在这种情况下,索引和表数据具有相同的结构,因此您没有索引查找的第二步。

我以两种不同的方式介绍了索引,首先是通过定义主键在users表上。 这将隐式在user_id列上创建唯一索引。 唯一索引意味着您不能两次插入相同的值集。 对于单列索引,这只意味着您不能两次具有相同的值。

如果您想象一本桌面的用户书,每页有一个用户,那么创建的索引会为您提供一个user_id的排序列表,每个列表都包含用户的页码。 该列表通常以某种树形式存储,以便快速查找特定数字。 考虑一下你在电话簿中查找名字的方式,你不仅要扫描所有页面,直到找到它,你猜测它会在哪里,然后跳过或转发大块的页面直到你接近。 您通常可以在O(log 2 n)时间内查找索引中的值,其中n是行数,您需要读取相似数量的索引页。

现在,如果给DB引擎查询select * from users Where user_id = 3 ,它有两个选择。 它可以读取每个数据页面,并查找正确的值(它可能会使用主键在第一个时停止的事实)。 另一种方法是读取索引以获取正确的数据页,然后查找数据页。

为了具体和简单,假设该表有1024个条目。 假设每个条目都占用一个数据页面。 假设索引树中的每个条目都占用一个索引页。 假设索引是平衡的,因此它有10个级别,总共2047个页面。 (所有这些假设都是可疑的,但是他们得到了相应的点,特别是索引页几乎总是小于数据页,因为你不倾向于一次索引所有列)。

要执行表扫描方法,需要读取1024个数据页。 要使用索引,需要读取10个索引页和一个数据页。 几乎所有数据库性能都与最小化读取页数有关。

多列索引允许快速查找数据集。 如果你有一个索引(col1,col2),即使只是匹配col1也会得到改进。

create index语句只是说明索引了哪些列,以及是否允许重复值。

再次使用本书类比, Create Index ix_comment_id on votes (comment_id, voter_id)将创建一个有序的comment_id列表,然后是voter_id,并引用相应的数据行。

+------------+--------------+---------+
| comment_id | reference_id | row_ref |
+------------+--------------+---------+
|          1 |            4 |    ref1 |
|          1 |            5 |    ref2 |
|          2 |            4 |    ref3 |
|          2 |            5 |    ref4 |
|          3 |            1 |    ref5 |
+------------+--------------+---------+

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM