Effective MySQL之SQL语句最优化--索引
1 两个索引取并集组合
-- 执行命令:
ALTER TABLE album ADD INDEX name_release (name,first_released);
EXPLAIN SELECT a.name, ar.name,
a.first_released
FROM album a
INNER JOIN artist ar USING (artist_id)
WHERE a.name = 'Greatest Hits'
ORDER BY a.first_released;/*执行显示结果*/
mysql> EXPLAIN SELECT a.name, ar.name,
-> a.first_released
-> FROM album a
-> INNER JOIN artist ar USING (artist_id)
-> WHERE a.name = 'Greatest Hits'
-> ORDER BY a.first_released;
+----+-------------+-------+--------+--------------------------------+--------------+---------+-------------------+------+-------------+| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------+--------+--------------------------------+--------------+---------+-------------------+------+-------------+
| 1 | SIMPLE | a | ref | name_release,name_2,name_part2 | name_release | 257 | const | 659 | Using where |
| 1 | SIMPLE | ar | eq_ref | PRIMARY | PRIMARY | 4 | union.a.artist_id | 1 | |
+----+-------------+-------+--------+--------------------------------+--------------+---------+-------------------+------+-------------+
2 rows in set (0.00 sec)ALTER TABLE album ADD INDEX name_release (name,first_released);
MySQL 可以在WHERE、ORDER BY 以及GROUP BY 列中使用索引;然而,一般来说MySQL 在一个表上只选择一个索引。
从MySQL 5.0 开始,在个别例外情况中优化器可能会使用一个以上的索引,但是在早期的版本中这样做会导致查询运行更加缓慢。
2 两个索引取并集
2.1 第一种: 最常见的索引合并的操作是两个索引取并集,当用户对两个有很
高基数的索引执行OR 操作时会出现这种这种索引合并操作。请
看下面的示例:
--执行命令:
SET @@session.optimizer_switch='index_merge_intersection=on';
EXPLAIN SELECT artist_id, name
FROM artist
WHERE name = 'Queen'
OR founded = 1942\G-- 执行过程:
mysql> EXPLAIN SELECT artist_id, name
-> FROM artist
-> WHERE name = 'Queen'
-> OR founded = 1942;
+----+-------------+--------+-------------+---------------+--------------+---------+------+------+----------------------------------------+| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+--------+-------------+---------------+--------------+---------+------+------+----------------------------------------+
| 1 | SIMPLE | artist | index_merge | name,founded | name,founded | 257,2 | NULL | 499 | Using union(name,founded); Using where |
+----+-------------+--------+-------------+---------------+--------------+---------+------+------+----------------------------------------+1 row in set (0.01 sec)
Extra: Using union(name,founded); 采用了union的联合索引模式,取合集.
注意
在MySQL 5.1 中首次引入了optimizer_switch 系统变量,可以
通过启用或禁用这个变量来控制这些附加选项。想了解更多信息可
以参考以下链接:http://dev.mysql.com/doc/refman/5.1/en/switchableoptimizations.html。
2.2 第二种类型的索引合并是对两个有少量唯一值的索引取交集,如下所示:
-- 执行命令:
SET @@session.optimizer_switch='index_merge_intersection=on';
EXPLAIN SELECT artist_id, name
FROM artist
WHERE type = 'Band'
AND founded = 1942;-- 执行过程:
mysql> SET @@session.optimizer_switch='index_merge_intersection=on';
Query OK, 0 rows affected (0.00 sec)mysql>
mysql>
mysql> EXPLAIN SELECT artist_id, name
-> FROM artist
-> WHERE type = 'Band'
-> AND founded = 1942;
+----+-------------+--------+------+---------------+---------+---------+-------+------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+--------+------+---------------+---------+---------+-------+------+-------------+
| 1 | SIMPLE | artist | ref | founded | founded | 2 | const | 498 | Using where |
+----+-------------+--------+------+---------------+---------+---------+-------+------+-------------+1 row in set (0.00 sec)
Extra: Using intersect(founded,type); Using where 这里由于是AND,所以只需要取2个索引中最高效的那个索引来进行遍历取值.
3 第三种类型的索引合并操作和对两个索引取并集比较类似,但它需要先经过排序:
-- 执行命令:
EXPLAIN SELECT artist_id, name
FROM artist
WHERE name = 'Queen'
OR (founded BETWEEN 1942 AND 1950);-- 执行过程:
mysql> EXPLAIN SELECT artist_id, name
-> FROM artist
-> WHERE name = 'Queen'
-> OR (founded BETWEEN 1942 AND 1950);
+----+-------------+--------+-------------+---------------+--------------+---------+------+------+---------------------------------------------+| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+--------+-------------+---------------+--------------+---------+------+------+---------------------------------------------+
| 1 | SIMPLE | artist | index_merge | name,founded | name,founded | 257,2 | NULL | 5900 | Using sort_union(name,founded); Using where |
+----+-------------+--------+-------------+---------------+--------------+---------+------+------+---------------------------------------------+
1 row in set (0.00 sec)
可以通过以下链接了解更多关于索引合并的信息:http://dev.mysql.com/doc/refman/5.5/en/index-merge-optimization.html。
4 数个索引合并的情况
在创建这些示例的过程中,还发现一种以前在任何客户端的查询中未曾出现过的新情况。以下是三个索引合并的示例:
-- 执行命令:
mysql> EXPLAIN SELECT artist_id, name
FROM artist
WHERE name = 'Queen'
OR (type = 'Band' AND founded = '1942');
.....-- 执行结果:
mysql> EXPLAIN SELECT artist_id, name
-> FROM artist
-> WHERE name = 'Queen'
-> OR (type = 'Band' AND founded = '1942');
+----+-------------+--------+-------------+---------------+--------------+---------+------+------+----------------------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+--------+-------------+---------------+--------------+---------+------+------+----------------------------------------+
| 1 | SIMPLE | artist | index_merge | name,founded | name,founded | 257,2 | NULL | 499 | Using union(name,founded); Using where |
+----+-------------+--------+-------------+---------------+--------------+---------+------+------+----------------------------------------+1 row in set (0.00 sec)
技巧
应该经常评估多列索引是否比让优化器合并索列效率更高。多个单列索引和多个多列索引到底哪个更有优势?这个问题
只有结合特定应用程序的查询类型和查询容量才能给出答案。在各种不同的查询条件下,将一些高基数列上的那些单列索引进行
索引合并能够带来很高的灵活性。数据库写操作的性能参考因素也同样会影响到获取数据的最优的数据访问路径。
5 创建更好的MySQL 索引
主要用的比较多的2个特殊的索引
通过使用索引,查询的执行时间可以从秒的数量级减少到毫秒数量级,这样的性能改进能够为你的应用程序的性能带来飞跃。
合理的调整你的索引对优化来说是非常重要的,尤其是对于高吞吐量的应用程序。即使对执行时间的改进仅仅是数毫秒,但对于
一个每秒执行1000 次的查询来说这也是非常有意义的性能提升。例如,把一个原本需要20 毫秒执行的每秒运行1 000 次的查询的
执行之间缩短4 毫秒,这对于优化SQL 语句来说是至关重要的。我们将使用第4 章介绍的方法创建多列索引,并在这一基础
上创建更好的覆盖索引。
● 创建覆盖索引
ALTER TABLE artist
DROP INDEX founded,
ADD INDEX founded_name (founded,name);
在InnoDB 中,主码的值会被附加在非主码索引的每个对应记录后面,因此没有必要在非主码索引中指定主码。
这一重要特性意味着InnoDB 引擎中所有非主码索引都隐含主码列了。并且对于那些从MyISAM 存储引擎转换过来的表,通常会
在它们InnoDB 表索引中将主码添加为最后一个元素。 当QEP 在Extra 列中显示Using index 时,这并不意味着在访
问底层表数据时使用到了索引,这表示只有这个索引才是满足查询所有要求的。这种索引可以为大型查询或者频繁执行的查询带
来显著的性能提升,它被称为覆盖索引。覆盖索引得名于它满足了查询中给定表用到的所有的列。想
要创建一个覆盖索引,这个索引必须包含指定表上包括WHERE语句、ORDER BY 语句、GROUP BY 语句(如果有的话)以及
SELECT 语句中的所有列。
[Comment]:随着数据容量的增加,尤其是超过内存和磁盘最大容量的时候,为一个大型列创建索引可能
会对系统整体性能有影响。覆盖索引对于那些使用了很多较小长度的主码和外键约束的大型规范化模式来说是理想的优化方式。
● 创建局部列的索引
-- 执行命令:
ALTER TABLE artist DROP INDEX name, ADD INDEX name_part(name(20));EXPLAIN SELECT artist_id,name,founded
FROM artist
WHERE name LIKE 'Queen%';-- 执行过程:
mysql> EXPLAIN SELECT artist_id,name,founded
-> FROM artist
-> WHERE name LIKE 'Queen%';
+----+-------------+--------+-------+---------------+------+---------+------+------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+--------+-------+---------------+------+---------+------+------+-------------+
| 1 | SIMPLE | artist | range | name | name | 257 | NULL | 93 | Using where |
+----+-------------+--------+-------+---------------+------+---------+------+------+-------------+1 row in set (0.00 sec)
这里主要考虑的是如何减小索引占用的空间。一个更小的索引意味着更少的磁盘I/O 开销,而这又意味着能更快地访问到需
要访问的行,尤其是当磁盘上的索引和数据列远大于可用的系统内存时。这样获得的性能改进将会超过一个非唯一的并且拥有低
基数的索引带来的影响。局部索引是否适用取决于数据是如何访问的。之前介绍覆盖索引时,你可以看到记录一个短小版本的name 列不会对执行过
的SQL 语句有任何好处。最大的益处只有当你在被索引的列上添加限制条件时才能体现出来。
-- 执行命令:
EXPLAIN SELECT artist_id,name,founded
FROM artist
WHERE name LIKE 'Queen%';-- 执行过程:
mysql> EXPLAIN SELECT artist_id,name,founded
-> FROM artist
-> WHERE name LIKE 'Queen%';
+----+-------------+--------+-------+---------------+------+---------+------+------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+--------+-------+---------------+------+---------+------+------+-------------+
| 1 | SIMPLE | artist | range | name | name | 257 | NULL | 93 | Using where |
+----+-------------+--------+-------+---------------+------+---------+------+------+-------------+1 row in set (0.00 sec)
在这个示例中,Extra后面没有出现Using Index,所以在索引中记录全名并没有带来额外的益处。
而所提供的局部列索引满足了WHERE 条件。如何选择合适的长度取决于数据的分布以及访问路径。目前没有准确的方法计算索
引的恰当长度。因此对给定范围的列长度内的唯一值数目的比较
是必不可少的。
count了下SELECT count(*) FROM artist WHERE name LIKE 'Queen%'; 才93条记录,而SELECT count(*) FROM artist;有577983条记录,按照普遍的情况,可以走索引,难道是name(20)的20定义的太长了?
ALTER TABLE artist
DROP INDEX name_part,
ADD INDEX name_part2(name(10));mysql> ALTER TABLE artist
-> DROP INDEX name_part,
-> ADD INDEX name_part2(name(10));
Query OK, 0 rows affected (3.41 sec)
Records: 0 Duplicates: 0 Warnings: 0mysql> EXPLAIN SELECT artist_id,name,founded
-> FROM artist
-> WHERE name LIKE 'Queen%';
+----+-------------+--------+-------+---------------+------------+---------+------+------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+--------+-------+---------------+------------+---------+------+------+-------------+
| 1 | SIMPLE | artist | range | name_part2 | name_part2 | 12 | NULL | 93 | Using where |
+----+-------------+--------+-------+---------------+------------+---------+------+------+-------------+1 row in set (0.00 sec)
看结果,再用name(5) 试试看。
mysql> ALTER TABLE artist
-> DROP INDEX name_part2,
-> ADD INDEX name_part3(name(5));
Query OK, 0 rows affected (3.21 sec)
Records: 0 Duplicates: 0 Warnings: 0mysql> EXPLAIN SELECT artist_id,name,founded
-> FROM artist
-> WHERE name LIKE 'Queen%';
+----+-------------+--------+-------+---------------+------------+---------+------+------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+--------+-------+---------------+------------+---------+------+------+-------------+
| 1 | SIMPLE | artist | range | name_part3 | name_part3 | 7 | NULL | 93 | Using where |
+----+-------------+--------+-------+---------------+------------+---------+------+------+-------------+1 row in set (0.00 sec)
看来局部索引对like的效果不是很明显的,可能跟数据分布范围有关,也许这93条数据全部打散在各个数据库块中,所以导致解析器认为不能简单地通过数次index就能遍历出数据,故而Extra栏里面就没有出现Using Index的提示。
总结:在索引中正确的定义列(包括定义列的顺序和位置)能够改变索引的实际使用效果。好的索引能够为一个执行缓慢的查询带来巨大的性能提升。索引也可能使原来执行很快的查询的执行时间减少若干毫秒。在高并发系统中,将1 000 000 条查询减少几毫秒将会显著改善性能,并且获得更大的容量和扩展性。为SQL 查询创建最优索引可以认为是一项艺术。
Refenerce : http://EffectiveMySQL.com
看完 Effective MySQL之SQL语句最优化 一书的索引章节记录.
Effective MySQL之SQL语句最优化--索引相关推荐
- Mysql中SQL语句不使用索引的情况
Mysql中SQL语句不使用索引的情况 MySQL查询不使用索引汇总 众所周知,增加索引是提高查询速度的有效途径,但是很多时候,即使增加了索引,查询仍然不使用索引,这种情况严重影响性能,这里就简单总结 ...
- mysql添加索引的方法(Navicat可视化加索引和sql语句加索引)
使用索引的场景: 阿里云日志里出现了慢sql 然后发现publish_works_id字段会经常用于一些关联,所以决定把这个字段加上索引,优化sql 可视化navicat操作字段加索引,选择字段所在的 ...
- mysql分析sql语句基础工具 —— explain
转载自 https://segmentfault.com/a/1190000009724144 立即登录 [笔记] mysql分析sql语句基础工具 -- explain mysql wateran ...
- mysql数据库语句分类_细数MySQL中SQL语句的分类
1:数据定义语言(DDL) 用于创建.修改.和删除数据库内的数据结构,如:1:创建和删除数据库(CREATE DATABASE || DROP DATABASE):2:创建.修改.重命名.删除表(C ...
- MySQL的SQL 语句:根据从表记录个数对主表排序
MySQL的SQL 语句:根据从表记录个数对主表排序 一个主表 news,有字段 nId(自动增长),sName. 记录: 10 name10 13 name13 20 ...
- php面试专题---MySQL常用SQL语句优化
php面试专题---MySQL常用SQL语句优化 一.总结 一句话总结: 原理,万变不离其宗:其实SQL语句优化的过程中,无非就是对mysql的执行计划理解,以及B+树索引的理解,其实只要我们理解执行 ...
- MySQL 查看SQL语句执行阶段和进度信息
MySQL 查看SQL语句执行阶段和进度信息 测试MySQL版本:5.7.31 根据<MySQL性能优化金字塔法则>做的实验,好书推荐! 我们在创建索引时,经常需要等待执行的SQL一段时间 ...
- mysql基本SQL语句
这篇文章主要介绍了Mysql数据库之sql基本语句以及作用,还有数据库.数据表等常见操作技巧,需要的朋友可以参考下 mysql基本SQL语句 1.常见命令 mysql -u root -p # 连接数 ...
- 检测mysql中sql语句的效率
1.通过查询日志 启动Mysql时加参数--log-slow-queries来记录执行时间超过long_query_time秒的sql MySQL 自带 slow log 的分析工具 mysqldum ...
最新文章
- 大厂也在用的 6种 数据脱敏方案,别做泄密内鬼
- 微信公众号数据2019_公众号年度数据报告怎么写?2019年公众号年报可一键生成啦!...
- 服务器挖chia用什么系统,Chia是什么?Chia挖矿服務器配置
- 活动目录最佳实践分析器
- VC遍历窗体控件的实现
- vue-router使用next()跳转到指定路径时会无限循环
- Linux基础知识part3
- mac下Clion与QT引入FFmpeg库
- python大型项目经验_经验丰富程序员才知道的8种高级Python技巧
- 远程访问CENTOS的MYSQL数据库设置
- 利用构造函数创建对象
- 苹果CMS v10模板:大橙子vfed完美版视频网站模板
- 如何创建强命名程序集
- python cli_测试Python命令行(CLI)应用程序的4种技术
- 植物大战僵尸-修改游戏进度和金币(用户存档文件)
- 基于python编程的激活码生成器
- 1.5.6.六种常见的三角关系
- TensorFlow TFRecords简介
- 我的世界服务器地图文件丢失,我的世界地图被毁了或找不到了 ? 大神手把手教你奇迹恢复...
- 第七十篇 数据处理与分析 Numpy