MySQL SQL优化之覆盖索引

内容概要

利用主索引提升SQL的查询效率是我们经常使用的一个技巧,但是有些时候MySQL给出的执行计划却完全出乎我们的意料,我们预想MySQL会通过索引扫描完成查询,但是MySQL给出的执行计划却是通过全表扫描完成查询的,其中的某些场景我们可以利用覆盖索引进行优化。

前些天,有个同事跟我说:“我写了个SQL,SQL很简单,但是查询速度很慢,并且针对查询条件创建了索引,然而索引却不起作用,你帮我看看有没有办法优化?”。

我对他提供的case进行了优化,并将优化过程整理了下来。

优化前的表结构、数据量、SQL、执行计划、执行时间

表结构

CREATE TABLE `t_order` (

`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,

`order_code` char(12) NOT NULL,

`order_amount` decimal(12,2) NOT NULL,

PRIMARY KEY (`id`),

UNIQUE KEY `uni_order_code` (`order_code`) USING BTREE )

ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;

隐藏了部分不相关字段后,可以看到表足够简单, 并且在order_code上创建了唯一性索引uni_order_code。

数据量:316977

这个数据量还是比较小的,不过如果SQL足够差,一样会查询很慢。

SQL

select order_code,

order_amount from t_order order by order_code limit 1000;

哇,SQL足够简单,不过有时候越简单也越难优化。

执行计划

全表扫描、文件排序,注定查询慢!

那为什么MySQL没有利用索引(uni_order_code)扫描完成查询呢?因为MySQL认为这个场景利用索引扫描并非最优的结果。我们先来看下执行时间,然后再来分析为什么没有利用索引扫描。

执行时间:260ms

的确,执行时间太长了,如果表数据量继续增长下去,性能会越来越差。

全表扫描、文件排序与索引扫描、索引排序的区别

全表扫描、文件排序:

虽然是全表扫描,但是扫描是顺序的(不管机械硬盘还是SSD顺序读写性能都是高的),并且数据量不是特别大,所以这部分消耗的时间应该不是特别大,主要的消耗应该是在排序上。

利用索引扫描、利用索引顺序:

uni_order_code是二级索引,索引上保存了(order_code,id),每扫描一条索引需要根据索引上的id定位(随机IO)到数据行上读取order_amount,需要1000次随机IO才能完成查询,而机械硬盘随机IO的效率是极低的(机械硬盘每秒寻址几百次)。

根据我们自己的分析选择全表扫描相对更优。如果把limit 1000改成limit 10,则执行计划会完全不一样。

既然我们已经知道是因为随机IO导致无法利用索引,那么有没有办法消除随机IO呢?

有,覆盖索引。

优化后的索引、执行计划、执行时间

创建索引

ALTER TABLE `t_order`

ADD INDEX `idx_ordercode_orderamount` USING BTREE (`order_code` ASC, `order_amount` ASC);

创建了复合索引idx_ordercode_orderamount(order_code,order_amount),将select的列order_amount也放到索引中。

执行计划

执行计划显示查询会利用覆盖索引,并且只扫描了1000行数据,查询的性能应该是非常好的。

执行时间:13ms

从执行时间来看,SQL的执行时间提升到原来的1/20,已经达到我们的预期。

总结

覆盖索引是select的数据列只用从索引中就能够取得,不必读取数据行,换句话说查询列要被所建的索引覆盖。索引的字段不只包含查询列,还包含查询条件、排序等。

要写出性能很好的SQL不仅需要学习SQL,还要能看懂数据库执行计划,了解数据库执行过程、索引的数据结构等。

转载自:Mr船长

原文:https://my.oschina.net/loujinhe/blog/1528233#comment-list

原文发布于微信公众号 - 数据和云(OraNews)

原文发表时间:2017-12-19

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏北京马哥教育

alter table锁表,MySQL出现Waiting for table metadata lock的场景浅析及解决方案

在修改/增加表字段的时候,发现很慢, show processlist; 时, Waiting for table metadata lock 能一直锁很久。 ...

3138
来自专栏架构师之路

InnoDB,5项最佳实践,知其所以然?

第一篇,说说MySQL两个最常用的存储引擎,MyISAM和InnoDB。照自己的理解,把一些知识点总结出来,不只说知识点,多讲“为什么”。 一、关于count...

652
来自专栏杨建荣的学习笔记

两个死锁的实例 (r5笔记第90天)

关于数据库中的死锁。如果在应用中碰到都会毫不犹豫转交给DBA,但是从目前我接到的deadlock的问题来看,和Oracle官方的描述基本都是一致的。 The f...

3016
来自专栏lonelydawn的前端猿区

MySQL应用优化

一、基本语句优化原则 (1).尽量避免在索引列上进行运算或函数操作,这样会导致索引失效 如: select * from t where Year(d)>=20...

1665
来自专栏Java进阶架构师

「mysql优化专题」单表查询优化的一些小总结,非索引设计(3)

(0)可以先使用 EXPLAIN 关键字可以让你知道MySQL是如何处理你的SQL语句的。这可以帮我们分析是查询语句或是表结构的性能瓶颈。

462
来自专栏大数据架构

SQL优化(六) MVCC PostgreSQL实现事务和多版本并发控制的精华

1685
来自专栏匠心独运的博客

大型分布式业务平台数据库优化方法(上)

文章摘要:一个小小的MySQL数据库B-Tree索引可能会带来意想不到的性能优化提升……

904
来自专栏个人随笔

MySQL 事物

事务是数据库处理操作,其中执行就好像它是一个单一的一组有序的工作单元。换言之,事务将永远不会是完全的,除非在组内每个单独的操作是成功的。如果事务中的任何操作失败...

3288
来自专栏Aloys的开发之路

oracle表空间表分区详解及oracle表分区查询使用方法(转+整理)

此文从以下几个方面来整理关于分区表的概念及操作: 1.表空间及分区表的概念 2.表分区的具体作用 3.表分区的优缺点 4.表分区的几种类型及操作方法 5...

2779
来自专栏岑玉海

Hive Tunning(三) 最佳实践

在上一讲的基础上,我们来做来一个实际的例子来展示如何在实操中进行高效的hive查询作业。 (1)首先我们建立一个表 CREATE EXTERNAL TA...

3567

扫码关注云+社区