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

Posted

技术标签:

【中文标题】何时在 SQL 表字段 (MySQL) 上添加索引?【英文标题】:When to add an index on a SQL table field (MySQL)? 【发布时间】:2012-11-06 15:20:11 【问题描述】:

有人告诉我,如果您知道自己会经常使用某个字段进行连接,那么最好在其上创建一个索引。

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

假设我有 3 个表:一个 USERS、COMMENTS 和一个 VOTES 表。我想创建一个类似 *** 的评论线程,其中查询返回 cmets 以及对这些 cmets 的赞成/反对票数。

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

Here's the query and SQLFiddle to return the votes on topic_id=1:

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;

让我们假设 cmets 和投票的数量达到数百万。为了加快查询速度,我的问题是我应该在comments.commenter_idvotes.voter_idvotes.comment_id 上添加索引吗?

【问题讨论】:

在加快查询速度时使用它们。 @SteveWellens 能否提供 SQL 代码以在答案和/或 SQLFiddle 中添加适当的索引? 您还需要在topic_id 上添加索引。 @Skpd,我对 -@SteveWellens 的评论相同... @SteveWellens,我感兴趣的只是让人们在 SQLFiddle 的基础上进行构建,显然您不必回答,我也很感谢您的 cmets,并不是有意冒犯 :)跨度> 【参考方案1】:

在 SQL 表中的何处使用索引并不总是很明确。但在大多数情况下,有一些通用的经验法则可能会帮助您做出决定。

    在 where 子句中使用的列上放置索引 为您用于连接的列添加索引。 尽量不要在同一个表的列上使用超过 4-5 个索引。

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

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

编辑

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

    不要在只有几个不同值的列上使用索引。例如,当只有少数状态时,保存状态的列或布尔值。不这样做的原因是索引并不能真正帮助您,因为它只会除以值的数量,并且由于您只有其中的几个,因此不会有任何真正的好处。该表会占用更多的索引空间,并且在插入时预制件会变慢,但在查询时不会获得明显更好的性能

【讨论】:

-@goBeepit dev,谢谢,你能在你的答案中添加一些代码来添加构建我正在使用的代码/SQLFiddle 的索引吗? 另外,不要为只采用几个离散值的列上的索引而烦恼。 @AndrewLazarus,所以这意味着不要在 vote 列(值= -1 或 1)上放置索引,但在 xxx_id 列上放置索引(其中值= 1- >无限)? @timpeterson 没错。顺序遍历投票列,选择大约一半,比对索引中(任一)值的开始和结束进行二进制搜索要快。即使您创建了一个索引,您的 RDMBS 也可能会根据其内部统计数据拒绝使用该索引。【参考方案2】:

这里有一些被使用的键的更新http://www.sqlfiddle.com/#!2/94daa/1

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

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

我不确定 mysql 是否有类似于 SQL Server 聚集索引的东西。这种情况下索引和表数据是同一个结构的,所以就不用进行第二步的索引查找了。

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

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

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

为了具体和简单起见,假设该表有 1024 个条目。假设每个条目占用一个数据页。假设索引树中的每个条目占用一个索引页。假设索引是平衡的,那么它有10个级别,总共2047页。 (所有这些假设都是可疑的,但它们的意思是交叉的,特别是索引页几乎总是比数据页小,因为您不倾向于一次索引所有列)。

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

多列索引允许快速查找数据集。如果你有一个带有 (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 |
+------------+--------------+---------+

【讨论】:

-@Laurence,我必须离开 2 小时,但之后我会检查您的代码并发表评论,谢谢您的回答! 我愿意接受您的回答,但为了完整起见,您能否先在回答中添加对代码中 CREATE INDEX 部分的解释? @timpeterson 添加了更多解释。 对于发现此问题的其他人,我发现在创建索引之前和之后比较性能的一个好方法是添加 use index() 这将告诉您的引擎在运行时不使用索引询问。查看this的答案

以上是关于何时在 SQL 表字段 (MySQL) 上添加索引?的主要内容,如果未能解决你的问题,请参考以下文章

MySQL如何为表字段添加索引

mysql索引?

SQL表字段如何建立索引?难道就是添加SQL查询语句?

mysql在建表语句中添加索引

mysql笔记-字段上的函数操作会使索引失效

MySQL(十八)—— 索引