MySQL索引 - 最佳实践是什么?

时间:2010-06-15 21:39:08

标签: mysql indexing query-optimization

我一直在我的MySQL数据库上使用索引一段时间但从未正确地学习关于它们。通常我会在我要搜索或使用WHERE子句选择的任何字段上放置索引,但有时它看起来不是那么黑白。

MySQL索引的最佳做法是什么?

示例情况/困境:

  

如果一个表有六列,那么全部   它们是可搜索的,我应该索引   所有这些还是没有?

  

有什么负面表现   索引的影响?

  

如果我有一个VARCHAR 2500列   可以从我的网站的部分搜索,   我应该索引吗?

7 个答案:

答案 0 :(得分:224)

你肯定应该花一些时间阅读索引,有很多关于它的文章,并且了解正在发生的事情很重要。

从广义上讲,索引会对表的行强制排序。

为简单起见,想象一个表只是一个大的CSV文件。每当插入一行时,它就会在末尾插入 。因此,表的“自然”排序只是插入行的顺序。

想象一下,您已经在一个非常基本的电子表格应用程序中加载了该CSV文件。所有这些电子表格都显示数据,并按顺序对行进行编号。

现在假设您需要在第三列中找到所有具有某些值“M”的行。鉴于您的可用性,您只有一个选项。您扫描表格,检查每行的第三列的值。如果你有很多行,这种方法(“表扫描”)可能需要很长时间!

现在想象一下,除了这个表,你还有一个索引。此特定索引是第三列中的值的索引。索引以一些有意义的顺序(例如,按字母顺序)列出第三列中的所有值,并且对于每个值,它提供了该值出现的行号列表。

现在您有一个很好的策略来查找第三列的值为“M”的所有行。例如,您可以执行binary search!虽然表扫描要求您查看N行(其中N是行数),但二进制搜索仅要求您查看log-n索引条目,在最坏的情况下。哇,这肯定容易多了!

当然,如果你有这个索引,并且你正在向表中添加行(最后,因为这是我们的概念表的工作原理),你需要每次都更新索引。因此,当您编写新行时,您会做更多的工作,但是当您搜索某些内容时,可以节省大量时间。

因此,通常,索引会在读取效率和写入效率之间进行权衡。没有索引,插入可以非常快 - 数据库引擎只是向表中添加一行。在添加索引时,引擎必须在执行插入时更新每个索引。

另一方面,读取变得更快。

希望这涵盖了你的前两个问题(正如其他人已经回答的那样 - 你需要找到合适的平衡点)。

你的第三个场景有点复杂。如果您使用LIKE,索引引擎通常会帮助您将读取速度提升到第一个“%”。换句话说,如果您正在选择WHERE列LIKE'foo%bar%',数据库将使用索引查找列以“foo”开头的所有行,然后需要扫描该中间行集以查找子集包含“bar”。 SELECT ... WHERE列LIKE'%bar%'无法使用索引。我希望你能明白为什么。

最后,您需要开始考虑多个列上的索引。概念是相同的,并且行为类似于LIKE的东西 - 基本上,如果你在(a,b,c)上有一个索引,引擎将尽可能从左到右继续使用索引。因此,对列a的搜索可能会使用(a,b,c)索引,就像(a,b)上的索引一样。但是,如果您搜索的是WHERE b = 5且c = 1,则引擎需要进行全表扫描。

希望这有助于提供一些亮点,但我必须重申,你最好花几个小时来寻找能够深入解释这些事情的好文章。阅读特定数据库服务器的文档也是一个好主意。查询规划者实现和使用索引的方式可以有很大的不同。

答案 1 :(得分:52)

查看More Mastering the Art of Indexing等演示文稿。

2012年12月更新:我发布了一个新的演示文稿:How to Design Indexes, Really。我于2012年10月在圣克拉拉的ZendCon和2012年12月在伦敦Percona Live举办了此活动。

设计最佳索引的过程必须与您在应用中运行的查询相匹配。

很难推荐任何关于哪些列最适合索引的通用规则,或者是否应该索引所有列,没有列,哪些索引应该跨越多列等等。这取决于您需要运行的查询。

是的,有一些开销,所以你不应该不必要地创建索引。但是创建索引,以便为快速运行所需的查询带来好处。索引的开销通常远远超过它的好处。

对于VARCHAR(2500)的列,您可能希望使用FULLTEXT index或前缀索引:

CREATE INDEX i ON SomeTable(longVarchar(100));

请注意,如果您正在搜索可能位于该长varchar中间的单词,则常规索引无法提供帮助。为此,请使用全文索引。

答案 2 :(得分:44)

我不会在其他答案中重复一些好的建议,但会补充:

复合指数

您可以创建复合索引 - 包含多个列的索引。 MySQL可以使用。所以如果你有:

Table A
Id
Name
Category
Age
Description

如果您的复合索引包含该顺序中的名称/类别/年龄,则这些WHERE子句将使用索引:

WHERE Name='Eric' and Category='A'

WHERE Name='Eric' and Category='A' and Age > 18

但是

WHERE Category='A' and Age > 18

不会使用该索引,因为必须从左到右使用所有内容。

<强>解释

使用Explain / Explain Extended来了解MySQL可用的索引以及它实际选择的索引。 MySQL每个查询只会使用 ONE

EXPLAIN EXTENDED SELECT * from Table WHERE Something='ABC'

慢查询日志

启用slow query log以查看哪些查询运行缓慢。

广告栏

如果您有一个宽列,其中MOST的区别在前几个字符中,则您只能使用索引中的前N个字符。示例:我们将ReferenceNumber列定义为varchar(255),但97%的情况下,参考编号为10个字符或更少。我将索引更改为仅查看前10个字符并提高了性能。

答案 3 :(得分:20)

  

如果一个表有六列并且所有这些列都是可搜索的,那么我应该将它们全部索引还是全部索引

您是在逐个字段的基础上搜索还是使用多个字段进行搜索? 哪些字段大多数正在搜索? 什么是字段类型? (例如,在INT上,索引比在VARCHAR上更好) 您是否尝试对正在运行的查询使用EXPLAIN?

  

索引的性能影响是什么?

UPDATE和INSERT会变慢。还有额外的存储空间要求,但这些日子通常不重要。

  

如果我有一个可从我网站的部分搜索的VARCHAR 2500列,我应该将其编入索引

不,除非它是UNIQUE(这意味着它已被索引)或者你只在该字段上搜索完全匹配(不使用LIKE或mySQL的全文搜索)。

  

通常我会在我将要搜索或使用WHERE子句选择的任何字段上放置一个索引

我通常索引查询次数最多的字段,然后是INTs / BOOLEANs / ENUM,而不是VARCHARS字段。不要忘记,通常需要在组合字段上创建索引,而不是在单个字段上创建索引。使用EXPLAIN,并检查慢速日志。

答案 4 :(得分:10)

有效加载数据:索引可加快检索速度,但会降低插入和删除速度,以及索引列中值的更新速度。也就是说,索引会减慢大多数涉及写入的操作。发生这种情况是因为写一行不仅需要写入数据行,还需要更改任何索引。表具有的索引越多,需要进行的更改越多,平均性能下降越大。大多数表都会接收大量读取和少量写入,但对于写入百分比较高的表,索引更新的成本可能会很高。

避免索引:如果您不需要特定索引来帮助查询更好地执行,请不要创建它。

磁盘空间:索引占用磁盘空间,多个索引会占用相应的空间。这可能会导致您比没有索引更快地达到表大小限制。尽可能避免使用索引。

外卖:不要过度指数

答案 5 :(得分:5)

通常,索引有助于加速数据库搜索,但缺点是使用额外的磁盘空间并减慢INSERT / UPDATE / DELETE查询速度。使用EXPLAIN并阅读结果以了解MySQL何时使用您的索引。

  

如果一个表有六列并且所有这些列都是可搜索的,我应该将它们全部索引还是不索引?

索引所有六列并不总是最佳做法。

(a)在搜索特定信息时,您是否会使用这些列?

(b)这些列的选择性是多少(与表中记录的总量相比,存储了多少个不同的值)?

MySQL使用基于成本的优化器,它试图在执行查询时找到“最便宜”的路径。选择性低的领域不是好的候选者。

  

索引的性能影响是什么?

已经回答:额外的磁盘空间,插入期间性能降低 - 更新 - 删除。

  

如果我有一个可从我网站的部分搜索的VARCHAR 2500列,我应该将其编入索引吗?

尝试FULLTEXT Index

答案 6 :(得分:4)

1/2)索引加速某些选择操作,但它们会减慢其他操作,如插入,更新和删除。它可以是一个很好的平衡。

3)使用全文索引或者使用sphinx