MySQL数据库优化之通过索引解决SQL性能问题


Posted in MySQL onApril 10, 2022

1.索引问题

索引是数据库优化中最常用也是最重要的手段之一,通过索引通常可以帮助用户解决大多数 的SQL性能问题。本章节将对MySQL中的索引的分类、存储、使用方法做详细的介绍。

2.索引的存储分类

MyISAM存储引擎的表数据和索引是自动分开存储的,各自是独立的一个文件;InnoDB存储引擎的表数据和索引是存储在同一个表空间里面,但可以有多个文件组成。MySQL中索引的存储类型目前只有两种(BTREE和HASH),具体和表的存储引擎相关:MyISAM和InnoDB存储引擎都只支持BTREE索引;MEMORY/HEAP存储引擎可以支持HASH和BTREE索引。MySQL目前不支持函数索引,但是能对列的前面某一部分进索引,例如上章节库存表goods_stock.LotNO批次字段,可以只取Model的前4个字符进行索引,这个特性可以大大缩小索引文件的大小,我们在设计表结构的时候也可以对文本列根据此特性进行灵活设计。下面是创建前缀索引的一个例子:

EXPLAIN SELECT * FROM goods_stock WHERE LotNO LIKE '2021%';

MySQL数据库优化之通过索引解决SQL性能问题

-- 创建前缀索引
CREATE INDEX idx_stock_2 ON goods_stock (LotNO(4));

MySQL数据库优化之通过索引解决SQL性能问题

3.如何使用索引

索引用于快速找出在某个列中有一特定值的行。对相关列使用索引是提高SELECT操作性能的最佳途径。查询要使用索引最主要的条件是查询条件中需要使用索引关键字,如果是多列索引,那么只有查询条件使用了多列关键字最左边的前缀时,才可以使用索引,否则将不能使用索引。

3.1使用索引

在MySQL中,下列几种情况下有可能使用到索引。
对于创建的多列索引,只要查询的条件中用到了最左边的列,索引一般就会被使用, 举例说明如下:

-- 首先在库存表goods_stock按Model,Brand的顺序创建一个复合索引CREATE INDEX idx_stock_1 ON goods_stock (Model,Brand);

然后按Model进行表查询,具体命令如下:

EXPLAIN SELECT * FROM goods_stock WHERE Model='LM358DT';

MySQL数据库优化之通过索引解决SQL性能问题
可以发现即便where条件中不是用Model与Brand字段的组合条件,索引仍然能用到,这就是索引的前缀特性(按照索引列顺序查询)。但是如果只按Brand条件查询表,那么索引就不会被用到,具体如下:

EXPLAIN SELECT * FROM goods_stock WHERE Brand='TI';

MySQL数据库优化之通过索引解决SQL性能问题
对于使用like的查询,后面如果是常量并且只有%号不在第一个字符,索引才可能会被使用,来看下面两个执行计划:

EXPLAIN SELECT * FROM goods_stock WHERE Model LIKE '%LM358';

MySQL数据库优化之通过索引解决SQL性能问题

EXPLAIN SELECT * FROM goods_stock WHERE Model LIKE 'LM358%';

MySQL数据库优化之通过索引解决SQL性能问题
可以发现第一个SQL没有使用索引,而第二个SQL就能够使用索引,区别就在于“%”的位置不同,前者把“%”放到第一位就不能用到索引,而后者没有放到第一位就使用了索引。另外,如果如果like后面跟的是一个列的名字,那么索引也不会被使用。如果对大的文本进行搜索,使用全文索引而不要使用like ‘%...%’。
如果列名是索引,使用column_name is null时候将会使用索引。如下例中查询LotNO为null的记录时候就会用到索引:

EXPLAIN SELECT * FROM goods_stock WHERE LotNO IS NULL;

MySQL数据库优化之通过索引解决SQL性能问题

3.2存在索引但不使用索引

在下列情况下,虽然存在索引,但是MySQL并不会使用相应的索引。
如果MySQL估计使用索引比全表扫描更慢,则不使用索引。例如如果列 key_part1 均匀分布在 1 和 100 之间,下列查询中使用索引就不是很好:

SELECT * FROM table_name where key_part1 > 1 and key_part1 < 90;

如果使用MEMORY/HEAP表并且where条件中不使用“=”进行索引列,那么不会用到索引。HEAP表只有在“=”的条件下才会使用索引。
用or分割开的条件,如果or前的条件中的列有索引,而后面的列中没有索引,那么涉及到的索引都不会被用到,例如:

SHOW INDEX FROM goods_stock;

MySQL数据库优化之通过索引解决SQL性能问题
通过命令可以看到goods_stock库存表有两个索引,然后我们再执行如下语句看是否使用索引:

EXPLAIN SELECT * FROM goods_stock WHERE LotNO='20200821' OR PackageUnit='包';

MySQL数据库优化之通过索引解决SQL性能问题
可见虽然在LotNO这个列上存在索引idx_stock_2,但是这个SQL语句并没有用到这个索引,原因就是or中有一个条件中的列没有索引。
如果列类型是字符串,那么一定记得在where条件中把字符常量值用引号引起来,否则即便这个列上有索引,MySQL也不会用到的,因为MySQL默认把输入的常量值进行转换以后才进行检索,请看如下例子:

DESC goods_stock;

MySQL数据库优化之通过索引解决SQL性能问题
通过DESC命令我们可以看到goods_stock库存表中的LotNO字段是字符型,如果我们在SQL语句中的LotNO字段加入一个数值型为20200821的条件值,因此即便在LotNO上有索引,MySQL也不能正确地用上索引,而是继续进行全表扫描,具体如下:

EXPLAIN SELECT * FROM goods_stock WHERE LotNO=20200821;

MySQL数据库优化之通过索引解决SQL性能问题

4.查看索引使用情况

如果索引正在工作,Handler_read_key的值将很高,这个值代表了一个行被索引值读的次数,很低的值表明增加索引得到的性能改善不高,因为索引并不经常使用。Handler_read_rnd_next的值高则意味着查询运行低效,并且应该建立索引补救。这个值的含义是在数据文件中读下一行的请求数。如果正进行大量的表扫描,Handler_read_rnd_next的值较高,则通常说明表索引不正确或写入的查询没有利用索引。可以先刷新状态再查询,具体如下:

FLUSH STATUS;
SHOW STATUS LIKE 'Handler_read%';

MySQL数据库优化之通过索引解决SQL性能问题
参数解释如下:
Handler_read_first:此选项表明SQL是在做一个全索引扫描,注意是全部,而不是部分,所以说如果存在WHERE语句,这个选项是不会变的。
Handler_read_key:此选项数值如果很高,MySQL高效的使用了索引,一切运转良好。
Handler_read_next:此选项表明在进行索引扫描时,按照索引从数据文件里取数据的次数。
Handler_read_prev:此选项表明在进行索引扫描时,按照索引倒序从数据文件里取数据的次数,一般就是ORDER BY … DESC。
Handler_read_rnd:就是查询直接操作了数据文件,很多时候表现为没有使用索引或者文件排序。
Handler_read_rnd_next:此选项值较高时候,则通常说明表索引不正确或写入的查询没有利用索引。

5.两个简单实用的优化方法

对于大多数开发人员来说,可能只希望掌握一些简单实用的优化方法,对于更多更复杂的优化,更倾向于交给专业DBA来做。本小节将向大家介绍两个简单适用的优化方法。

5.1定期分析表和检查表

分析表的语法如下:

ANALYZE [LOCAL | NO_WRITE_TO_BINLOG] TABLE tbl_name [, tbl_name] ...

本语句用于分析和存储表的关键字分布,分析的结果将可以使得系统得到准确的统计信息,使得SQL能够生成正确的执行计划。如果用户感觉实际执行计划并不是预期的执行计划,执行一次分析表可能会解决问题。在分析期间,使用一个读取锁定对表进行锁定。这对于MyISAM, BDB和InnoDB表有作用。对于MyISAM表,本语句与使用myisamchk -a相当,下例中对goods_stock表做了表分析:

ANALYZE TABLE goods_stock;

MySQL数据库优化之通过索引解决SQL性能问题
●检查表的语法如下:

CHECK TABLE tbl_name [, tbl_name] ... [option] ... option = {QUICK | FAST | MEDIUM | EXTENDED | CHANGED}

检查表的作用是检查一个或多个表是否有错误。CHECK TABLE对MyISAM和InnoDB表有作用。对于MyISAM表,关键字统计数据被更新,例如:

CHECK TABLE goods_stock;

MySQL数据库优化之通过索引解决SQL性能问题
CHECK TABLE也可以检查视图是否有错误,比如在视图定义中被引用的表已不存在,举例如下:
(1)首先我们创建一个表。

CREATE TABLE test
(
  ID INT(11)
);

(2)再创建一个视图。

CREATE VIEW test_view AS SELECT * FROM test;

(3)然后CHECK一下该视图,发现没有问题。

CHECK TABLE test_view;

MySQL数据库优化之通过索引解决SQL性能问题
(4)现在删除掉视图依赖的表。

DROP TABLE test;

(5)再来CHECK一下刚才的视图,发现报错了。

CHECK TABLE test_view;

MySQL数据库优化之通过索引解决SQL性能问题

5.2定期优化表

优化表的语法如下:

OPTIMIZE [LOCAL | NO_WRITE_TO_BINLOG] TABLE tbl_name [, tbl_name] ...

如果已经删除了表的一大部分,或者如果已经对含有可变长度行的表(含有VARCHAR、BLOB或TEXT列的表)进行了很多更改,则应使用OPTIMIZE TABLE 命令来进行表优化。这个命令可以将表中的空间碎片进行合并,并且可以消除由于删除或者更新造成的空间浪费,但OPTIMIZE TABLE命令只对MyISAM、BDB和InnoDB表起作用。以下例子显示了优化goods_stock库存表的过程:

-- 先查看下goods_stock库存表是什么表类型
SHOW TABLE STATUS LIKE 'goods_stock%';

MySQL数据库优化之通过索引解决SQL性能问题

OPTIMIZE TABLE goods_stock;

MySQL数据库优化之通过索引解决SQL性能问题
注意:ANALYZE、CHECK、OPTIMIZE执行期间将对表进行锁定,因此一定注意要在数据库不繁忙的情况下执行相关的操作。

参考文献:
深入浅出MySQL大全

MySQL 相关文章推荐
MySQL 重写查询语句的三种策略
May 10 MySQL
Navicat for MySQL的使用教程详解
May 27 MySQL
Mysql 设置boolean类型的操作
Jun 04 MySQL
MySQL面试题讲解之如何设置Hash索引
Nov 01 MySQL
分享mysql的current_timestamp小坑及解决
Nov 27 MySQL
JMeter对MySQL数据库进行压力测试的实现步骤
Jan 22 MySQL
实战 快速定位MySQL的慢SQL
Mar 22 MySQL
pt-archiver 主键自增
Apr 26 MySQL
MySQL 数据表操作
May 04 MySQL
mysql性能优化以及配置连接参数设置
May 06 MySQL
MySQL导致索引失效的几种情况
Jun 25 MySQL
Mysql中mvcc各场景理解应用
Aug 05 MySQL
MySQL 外连接语法之 OUTER JOIN
Apr 09 #MySQL
MySQL中优化SQL语句的方法(show status、explain分析服务器状态信息)
Apr 09 #MySQL
进阶篇之linux环境下安装MySQL数据库
MySQL的存储函数与存储过程的区别解析
Apr 08 #MySQL
MySQL数据库查询进阶之多表查询详解
MySQL中一条SQL查询语句是如何执行的
解决MySQL Varchar 类型尾部空格的问题
Apr 06 #MySQL
You might like
PHP Session 变量的使用方法详解与实例代码
2013/09/11 PHP
浅析PHP关键词替换的类(避免重复替换,保留与还原原始链接)
2015/09/22 PHP
Yii2 rbac权限控制操作步骤实例教程
2016/04/29 PHP
PHP 布尔值的自增与自减的实现方法
2018/05/03 PHP
php生成word并下载代码实例
2019/03/15 PHP
laravel 中某一字段自增、自减的例子
2019/10/11 PHP
静态的动态续篇之来点XML
2006/08/15 Javascript
jQuery仿Excel表格编辑功能的实现代码
2013/05/01 Javascript
jQuery固定浮动侧边栏实现思路及代码
2014/09/28 Javascript
JavaScript实现解析INI文件内容的方法
2016/11/17 Javascript
webpack2.0搭建前端项目的教程详解
2017/04/05 Javascript
使用Webpack提高Vue.js应用的方式汇总(四种)
2017/07/10 Javascript
使用JS实现气泡跟随鼠标移动的动画效果
2017/09/16 Javascript
Vue+SpringBoot开发V部落博客管理平台
2017/12/27 Javascript
angular项目中bootstrap-datetimepicker时间插件的使用示例
2018/03/15 Javascript
详解vue中localStorage的使用方法
2018/11/22 Javascript
JavaScript错误处理操作实例详解
2019/01/04 Javascript
vue路由传参的基本实现方式小结【三种方式】
2020/02/05 Javascript
基于Echarts图表在div动态切换时不显示的解决方式
2020/07/20 Javascript
[00:35]TI7不朽珍藏III——寒冰飞龙不朽展示
2017/07/15 DOTA
python去除字符串中的换行符
2017/10/11 Python
python Pandas库基础分析之时间序列的处理详解
2019/07/13 Python
浅谈图像处理中掩膜(mask)的意义
2020/02/19 Python
python相对企业语言优势在哪
2020/06/12 Python
世界知名接发和假发品牌:Poze Hair
2017/03/08 全球购物
C和C++经典笔试题附答案解析
2014/08/18 面试题
最新党员的自我评价分享
2013/11/04 职场文书
大一自我鉴定范文
2013/12/27 职场文书
酒店大堂副理的职责范文
2014/02/13 职场文书
货物运输服务质量承诺书
2014/05/29 职场文书
2014迎接教师节演讲稿
2014/09/10 职场文书
领导四风问题整改措施思想汇报
2014/10/13 职场文书
质量保证书
2015/01/17 职场文书
辩论赛主持人开场白
2015/05/29 职场文书
Python连接Postgres/Mysql/Mongo数据库基本操作大全
2021/06/29 Python
《异世界四重奏》剧场版6月10日上映 PV视觉图原创角色发表
2022/03/20 日漫