3.杨明翰的MySQL教学系列之索引篇

MySQL教学系列 专栏收录该内容
5 篇文章 1 订阅


前言

索引是一个非常影响性能的元素,是提高数据库查询性能的常用&重要方法,
因此很多公司都喜欢问面试者关于索引的问题。
索引这块的知识点不难(想想锁机制吧),是必须要通关的游戏,
理解为主,记忆为辅。

索引有助于分析与解决生产环境的问题。
重点在于单列索引与多列索引,以及最左原则,
还有什么情况下应该建索引,什么情况下不应该建索引。


什么是索引

创建索引是为了加速对表的查询,
索引通过快速路径访问方法来快速定位数据,从而减少磁盘IO。

可以把索引理解成一本书的目录,例如一本英文字典,我要寻找warcraft这个单词,我首先会通过目录找到w字母的页,然后再去找w字母后面的a字母,
而不是从第一页开始翻,一直翻到warcraft这页。

从第一页开始翻的这种类似于全表扫描,性能非常low,就算你一个很简单的表结构,
每次单表查询都会检索几十万条数据,也是一个性能灾难,
高并发场景下会造成cpu飙高、会话超时等一系列问题。

索引用来快速地寻找那些具有特定值的记录。
如果没有索引,执行查询时MySQL必须从第一个记录开始扫描整个表的所有记录,
直至找到符合要求的记录。表里面的记录数量越多,这个操作的代价就越高。
如果作为搜索条件的列上已经创建了索引,
MySQL无需扫描任何记录即可迅速得到目标记录所在的位置。

在没有给a列创建索引时,a列没有明确的次序,给a列建索引后,
mysql将在索引中排序a列。

mysql内部保存一个数据文件记录实际记录所在位置的"指针"(方便索引快速回查到原记录)。

我们按a列进行where条件查询,mysql能够在a列的索引中非常快速找到值,
然后直接转到数据文件中相应的行,将该行返回。

在这个过程中,mysql只需要处理一个行就可以,否则将扫描整个表的所有记录。

个人建议不要刚上来就自己创建索引,而是数据量到达一定规模后,
通过分析工具来检测哪些列需要真正意义上的需要创建索引。

mysql使用information_schema库里的STATISTICS来保存索引信息。


创建&删除索引的方式

创建&删除索引的方式分为两种:

自动

当在表中定义主键约束、唯一约束、外键约束时,
系统会自动建立相应的索引。数据表被删除时,表上的索引自动被删除。

手动

通过create index语句来创建索引。通过drop index语句来删除索引。
(DROP INDEX 索引名 ON 表名)


索引的类型

普通索引(normal)

这是最基本的索引类型,而且它没有唯一性之类的限制。

#创建普通索引
CREATE INDEX <索引的名字> ON tablename (列的列表);

#修改普通索引
ALTER TABLE tablename ADD INDEX [索引的名字] (列的列表);

#创建表时指定
CREATE TABLE tablename ( [...], INDEX [索引的名字] (列的列表) );

唯一索引(unique)

这种索引和前面的“普通索引”基本相同,但有一个区别:索引列的所有值都只能出现一次,即必须唯一,允许有空值。

#创建唯一索引
CREATE UNIQUE INDEX <索引的名字> ON tablename (列的列表);

#修改唯一索引
ALTER TABLE tablename ADD UNIQUE [索引的名字] (列的列表);

#创建表时指定
CREATE TABLE tablename ( [...], UNIQUE [索引的名字] (列的列表) );

主键索引(primary key)

主键是一种唯一索引,每个表只能有一个主键,不允许重复,不允许为空。

#创建主键索引
CREATE TABLE tablename ( [...], PRIMARY KEY (列的列表) );

#修改主键索引
ALTER TABLE tablename ADD PRIMARY KEY (列的列表);

全文索引(full text)

全文索引可以在VARCHAR或者TEXT类型的列上创建。
全文索引不展开,可搜索其他文章。
不建议创建全文索引,如果有搜索场景请使用elastic search等开源框架。


单列索引与多列索引

索引可以建立在单列上,也可以建立在多列上。
假设有这样一个people表:

这个数据片段中有四个名字为“Mikes”的人(其中两个姓Sullivans,两个姓McConnells),有两个年龄为17岁的人,还有一个名字与众不同的Joe Smith。

这个表的主要用途是根据指定的用户姓、名以及年龄返回相应的peopleid。例如,我们可能需要查找姓名为Mike Sullivan、年龄17岁用户的peopleid(SQL命令为SELECT peopleid FROM people WHERE firstname=‘Mike’ AND lastname=‘Sullivan’ AND age=17;)。
由于我们不想让MySQL每次执行查询就去扫描整个表,这里需要考虑运用索引。

首先,我们可以考虑在单个列上创建索引,比如firstname、lastname或者age列。
如果我们创建firstname列的索引(ALTER TABLE people ADD INDEX firstname (firstname);),
MySQL将通过这个索引迅速把搜索范围限制到那些firstname='Mike’的记录,
然后再在这个“中间结果集”上进行其他条件的搜索:它首先排除那些lastname不等于“Sullivan”的记录,
然后排除那些age不等于17的记录。当记录满足所有搜索条件之后,MySQL就返回最终的搜索结果。

由于建立了firstname列的索引,与执行表的完全扫描相比,MySQL的效率提高了很多,但我们要求MySQL扫描的记录数量仍旧远远超过了实际所需要的。虽然我们可以删除firstname列上的索引,再创建lastname或者age列的索引,但总地看来,不论在哪个列上创建索引搜索效率仍旧相似。

为了提高搜索效率,我们需要考虑运用多列索引。如果为firstname、lastname和age这三个列创建一个多列索引,MySQL只需一次检索就能够找出正确的结果!下面是创建这个多列索引的SQL命令:

ALTER TABLE people ADD INDEX fname_lname_age (firstname,lastname,age);

由于索引文件以B-树格式保存,MySQL能够立即转到合适的firstname,然后再转到合适的lastname,最后转到合适的age。在没有扫描数据文件任何一个记录的情况下,MySQL就正确地找出了搜索的目标记录!

那么,如果在firstname、lastname、age这三个列上分别创建单列索引,
效果是否和创建一个firstname、lastname、age的多列索引一样呢?答案是否定的,两者完全不同。
当我们执行查询的时候,MySQL只能使用一个索引。如果你有三个单列的索引,
MySQL会试图选择一个限制最严格的索引。但是,即使是限制最严格的单列索引,
它的限制能力也肯定远远低于firstname、lastname、age这三个列上的多列索引。


最左原则

多列索引还有另外一个优点,它通过称为最左前缀(Leftmost Prefixing)的概念体现出来。
现在我们有一个firstname、lastname、age列上的多列索引,我们称这个索引为fname_lname_age。它相当于我们创建了(firstname,lastname,age)、(firstname,lastname)以及(firstname)这些列组合上的索引。

#下面这些查询都能够使用fname_lname_age索引:
SELECT peopleid FROM people WHERE firstname='Mike' AND lastname='Sullivan' AND age='17';
SELECT peopleid FROM people WHERE firstname='Mike' AND lastname='Sullivan';
SELECT peopleid FROM people WHERE firstname='Mike';

#下面这些查询都不能够使用fname_lname_age索引 :
SELECT peopleid FROM people WHERE lastname='Sullivan';
SELECT peopleid FROM people WHERE age='17';
SELECT peopleid FROM people WHERE lastname='Sullivan' AND age='17';
SELECT peopleid FROM people WHERE firstname='Mike' AND age='17';

换句话说,你建立的多列索引,只能从最左边依次开始算,如果顺序不对,则索引失效。


索引的缺点

1.索引要占用磁盘空间。通常情况下,这个问题不是很突出。
但是,如果你创建每一种可能列组合的索引,索引文件体积的增长速度将远远超过数据文件。
如果你有一个很大的表,索引文件的大小可能达到操作系统允许的最大文件限制。

2.对于需要写入数据的操作,比如DELETE、UPDATE以及INSERT操作,
索引会降低它们的速度。这是因为MySQL不仅要把改动数据写入数据文件,
而且它还要把这些改动写入索引文件。

(在设计和创建索引时,应确保对性能的提高程度大于在存储空间和处理资源方面的代价。 )


对哪些列建立索引

一般说来,索引应建立在那些将用于JOIN, WHERE和ORDER BY排序的字段上。

尽量不要对数据库中某个含有大量重复的值的字段建立索引。

例如customerinfo中的“province”… 字段,在这样的字段上建立索引将不会有什么帮助;
相反,还有可能降低数据库的性能。

在建有索引的字段上尽量不要使用函数进行操作。

例如,在一个DATE类型的字段上使用YEAE()函数时,将会使索引不能发挥应有的作用。
所以,下面的两个查询虽然返回的结果一样,但后者要比前者快得多。

SELECT * FROM order WHERE YEAR(OrderDate) < 2001;
SELECT * FROM order WHERE OrderDate<"2001-01-01";(快的多)
在Join表的时候使用相同类型的例,并将其索引。

如果你的应用程序有很多 JOIN 查询,你应该确认两个表中Join的字段是被建过索引的。这样,MySQL内部会启动为你优化Join的SQL语句的机制。而且,这些被用来Join的字段,应该是相同的类型的。例如:如果你要把 DECIMAL 字段和一个 INT 字段Join在一起,MySQL就无法使用它们的索引。对于那些STRING类型,还需要有相同的字符集才行。(两个表的字符集有可能不一样)

不要在小型表创建索引

因为在检索索引数据时,时间可能比扫描整个表的时间还要长。

不要在频繁更新的列上创建索引。

总结

索引是一个非常重要的概念,索引跟后面的innodb引擎下的锁机制有着千丝万缕的联系,
知识都是一环套一环的,并且索引用好了与用不好,差距还是蛮大的。
希望大家能把索引的功力发挥到最大。

  • 0
    点赞
  • 0
    评论
  • 0
    收藏
  • 一键三连
    一键三连
  • 扫一扫,分享海报

相关推荐
©️2020 CSDN 皮肤主题: 精致技术 设计师:CSDN官方博客 返回首页
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、C币套餐、付费专栏及课程。

余额充值