浅谈MySQL索引优化

一、索引简介

什么是索引?

  • MySQL官方对索引的定义为:索引(Index)是帮助MySQL高效获取数据的数据结构。

  • 可以简单理解为“排好序的快速查找数据结构”。

  • 一般来说索引本身也很大,不可能全部存储在内存中,因此索引往往以索引文件的形式存储的磁盘上

索引的优点

  • 类似大学图书馆建书目索引,提高数据检索的效率,降低数据库的IO成本

  • 通过索引列对数据进行排序,降低数据排序的成本,降低了CPU的消耗

索引的缺点

  • 虽然索引大大提高了查询速度,同时却会降低更新表的速度,如对表进行INSERT、UPDATE和DELETE。因为更新表时,MySQL不仅要保存数据,还要保存一下索引文件每次更新添加了索引列的字段,都会调整因为更新所带来的键值变化后的索引信息

  • 实际上索引也是一张表,该表保存了主键与索引字段,并指向实体表的记录,所以索引列也是要占用空间的

索引结构

MySQL支持诸多存储引擎,而各种存储引擎对索引的支持也各不相同,因此MySQL数据库支持多种索引类型,如BTree索引,哈希索引,全文索引等等。

下面简单介绍下BTree索引、B+Tree索引、聚簇索引与非聚簇索引

  • BTree索引

  • B+Tree索引

InnoDB使用B+Tree作为索引结构

拓展:B+Tree与B-Tree 的区别

1)B树的关键字和记录是放在一起的,叶子节点可以看作外部节点,不包含任何信息;B+树的非叶子节点中只有关键字和指向下一个节点的索引,记录只放在叶子节点中。

2)在B树中,越靠近根节点的记录查找时间越快,只要找到关键字即可确定记录的存在;而B+树中每个记录的查找时间基本是一样的,都需要从根节点走到叶子节点,而且在叶子节点中还要再比较关键字。从这个角度看B树的性能好像要比B+树好,而在实际应用中却是B+树的性能要好些。因为B+树的非叶子节点不存放实际的数据,这样每个节点可容纳的元素个数比B树多,树高比B树小,这样带来的好处是减少磁盘访问次数。尽管B+树找到一个记录所需的比较次数要比B树多,但是一次磁盘访问的时间相当于成百上千次内存比较的时间,因此实际中B+树的性能可能还会好些,而且B+树的叶子节点使用指针连接在一起,方便顺序遍历(例如查看一个目录下的所有文件,一个表中的所有记录等),这也是很多数据库和文件系统使用B+树的缘故。
 

思考:为什么说B+树比B树更适合实际应用中操作系统的文件索引和数据库索引?

1) B+树的磁盘读写代价更低
  
B+树的内部结点并没有指向关键字具体信息的指针。因此其内部结点相对B 树更小。如果把所有同一内部结点的关键字存放在同一盘块中,那么盘块所能容纳的关键字数量也越多。一次性读入内存中的需要查找的关键字也就越多。相对来说IO读写次数也就降低了。
2) B+树的查询效率更加稳定
  
由于非终结点并不是最终指向文件内容的结点,而只是叶子结点中关键字的索引。所以任何关键字的查找必须走一条从根结点到叶子结点的路。所有关键字查询的路径长度相同,导致每一个数据的查询效率相当。

  • 聚簇索引与非聚簇索引

聚簇索引并不是一种单独的索引类型,而是一种数据存储方式。
术语‘聚簇’表示数据行和相邻的键值聚簇的存储在一起。
如下图,左侧的索引就是聚簇索引,因为数据行在磁盘的排列和索引排序保持一致。

聚簇索引的好处:

按照聚簇索引排列顺序,查询显示一定范围数据的时候,由于数据都是紧密相连,数据库不不用从多个数据块中提取数据,所以节省了大量的io操作。

聚簇索引的限制:

对于mysql数据库目前只有innodb数据引擎支持聚簇索引,而Myisam并不支持聚簇索引。
由于数据物理存储排序方式只能有一种,所以每个Mysql的表只能有一个聚簇索引。一般情况下就是该表的主键。
为了充分利用聚簇索引的聚簇的特性,所以innodb表的主键列尽量选用有序的顺序id,而不建议用无序的id,比如uuid这种。

MySQL索引分类

  • 单值索引

即一个索引只包含单个列,一个表可以有多个单列索引

单独建单值索引:

CREATE INDEX idx_customer_name ON customer(customer_name);

删除索引:

DROP INDEX idx_customer_name on customer;

  • 唯一索引

索引列的值必须唯一,但允许有空值

单独建唯一索引:

CREATE UNIQUE INDEX idx_customer_no ON customer(customer_no);

删除索引:

DROP INDEX idx_customer_no on customer;

  • 主键索引

设定为主键后数据库会自动建立索引,innodb为聚簇索引

单独建主键索引:

ALTER TABLE customer add PRIMARY KEY customer(customer_no);

删除建主键索引:

ALTER TABLE customer drop PRIMARY KEY;

  • 复合索引

即一个索引包含多个列

单独建索引:

CREATE INDEX idx_no_name ON customer(customer_no,customer_name);

删除索引:

DROP INDEX idx_no_name on customer;

  • 基本语法总结

创建:

CREATE [UNIQUE ] INDEX [indexName] ON table_name(column));

删除:

DROP INDEX [indexName] ON mytable;

查看:

SHOW INDEX FROM table_name;


还可以使用ALTER命令

哪些情况需要创建索引?☆

  • 主键自动建立唯一索引

  • 频繁作为查询条件的字段应该创建索引

  • 查询中与其它表关联的字段,外键关系建立索引

  • 单键/组合索引的选择问题, 组合索引性价比更高

  • 查询中排序的字段,排序字段若通过索引去访问将大大提高排序速度

  • 查询中统计或者分组字段

哪些情况不要创建索引?☆

  • 表记录太少

  • 经常增删改的表或者字段

虽然提高了查询速度,但是会降低更新表的速度,如对表进行INSERT、UPDATE和DELETE。更新表时,MySQL不仅要保存数据,还要保存一下索引文件。

  • Where条件里用不到的字段不创建索引

  • 过滤性不好的不适合建索引

二、借助Explain进行性能分析

Explain是什么(查看执行计划)

  • 使用EXPLAIN关键字可以模拟优化器执行SQL查询语句,从而知道MySQL是如何处理你的SQL语句的。分析你的查询语句或是表结构的性能瓶颈。

Explain用途

  • 查看表的读取顺序

  • 查看哪些索引可以使用

  • 查看数据读取操作的操作类型

  • 查看哪些索引被实际使用

  • 查看表之间的引用

  • 查看每张表有多少行被物理查询

Explain使用方法

  • Explain + SQL语句

  • 执行计划包含的信息:

各字段解释:☆

  • id

select查询的序列号,包含一组数字,表示查询中执行select子句或操作表的顺序

id相同,执行顺序由上至下


id不同,如果是子查询,id的序号会递增,id值越大优先级越高,越先被执行


id相同不同,同时存在


每个id号码,表示一趟独立的查询。一个sql的查询趟数越少越好。

 

  • select_type

查询的类型,主要是用于区别普通查询、联合查询、子查询等的复杂查询

  • table

    显示这一行的数据是关于哪张表的

  • partitions

    代表分区表中的命中情况,非分区表,该项为null

  • type

type显示的是访问类型,是较为重要的一个指标,结果值从最好到最坏依次是:

system > const > eq_ref > ref > fulltext > ref_or_null > index_merge > unique_subquery > index_subquery > range > index > ALL

一般来说,得保证查询至少达到range级别,最好能达到ref。

 

  • possible_keys

    显示可能应用在这张表中的索引,一个或多个。
    查询涉及到的字段上若存在索引,则该索引将被列出,但不一定被查询实际使用。

  • key

    实际使用的索引。如果为NULL,则没有使用索引。查询中若使用了覆盖索引,则该索引和查询的select字段重叠。

  • key_len

表示索引中使用的字节数,可通过该列计算查询中使用的索引的长度。key_len字段能够帮你检查是否充分的利用了索引。

  • ref

    显示索引的哪一列被使用了,如果可能的话,是一个常数。哪些列或常量被用于查找索引列上的值

  • rows

    rows列显示MySQL认为它执行查询时必须检查的行数。rows越小,性能越高。

  • filtered

    这个字段表示存储引擎返回的数据在server层过滤后,剩下多少满足查询的记录数量的比例,注意是百分比,不是具体记录数

  • Extra

包含不适合在其他列中显示但十分重要的额外信息

三、查询优化策略☆

单表优化策略

  • 全列匹配

  • 最左前缀法则

    如果索引了多列,要遵守最左前缀法则。指的是查询从索引的最左前列开始命中并且不跳过索引中的列。

    注意:由于MySQL的查询优化器会自动调整where子句的条件顺序以使用适合的索引。

  • 不要在索引列上做任何操作(计算、函数、(自动or手动)类型转换),会导致索引失效而转向全表扫描

  • 存储引擎不能使用索引中范围条件右边的列

  • mysql 在使用不等于(!= 或者<>)的时候无法使用索引,会导致全表扫描

  • is not null 无法使用索引,但is null是可以使用索引的

  • 若like以通配符开头('%abc…'),则mysql索引失效,会变成全表扫描的操作

  • 字符串不加单引号索引失效

  • 练习

 

 

  • 对于单键索引,尽量选择针对当前query过滤性更好的索引

  • 在选择组合索引的时候,当前Query中过滤性最好的字段在索引字段顺序中,位置越靠前越好。

  • 在选择组合索引的时候,尽量选择可以能够包含当前query中的where字句中更多字段的索引

  • 在选择组合索引的时候,如果某个字段可能出现范围查询时,尽量把这个字段放在索引次序的最后面

  • 书写sql语句时,尽量避免造成索引失效的情况

多表关联查询优化策略

  • 保证被驱动表的join字段已经被索引

  • left join 时,选择小表作为驱动表,大表作为被驱动表。

  • inner join 时,mysql会自己帮你把小结果集的表选为驱动表。

  • 子查询尽量不要放在被驱动表,有可能使用不到索引。

  • 能够直接多表关联的尽量直接关联,不用子查询。

子查询优化

  • 尽量不要使用not in或者not exists。用left outer join on xxx is null 替代。

排序分组优化

  • ORDER BY子句,尽量使用Index方式排序,避免使用FileSort方式排序

  • 当范围条件是group by 或者 order by 的字段出现二选一时 ,优先观察条件字段的过滤数量,如果过滤的数据足够多,而需要排序的数据并不多时,优先把索引放在范围字段上。反之,亦然。

  • 如果不在索引列上,filesort有两种算法:
    mysql就要启动双路排序和单路排序

  • GROUP BY关键字优化

group by 使用索引的原则几乎跟order by一致 ,唯一区别是groupby 即使没有过滤条件用到索引,也可以直接使用索引。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值