SQL 语句执行时间长,效率低的原因可能有如下几条:
由于 MySQL 会对 SQL 语句进行优化的原因,我们写的 SQL 语句和数据库引擎执行时的语句可能有所不能。比如我们写的 SQL 语句为:
select distinct
<select_list>
from
<left_table> <join_type> join <right_table> on <join_condition>
where
<where_condition>
group by
<group_by_list>
having
<having_condition>
order by
<order_by_condition>
limit <limit_number>;
而 MySQL 翻译后的语句可能为:
from <left_table>
on <join_condition>
<join_type> join <right_table>
where <where_condition>
group by <group_by_list>
having <having_condition>
select distinct <select_list>
order by <order_by_condition>
limit <limit_number>;
SQL 语句的执行顺序可以归结为如下图:
MySQL官方对索引的定义为:索引(Index)是帮助MySQL高效获取数据的数据结构。
可以得到索引的本质:索引是数据结构。
索引的目的在于提高查询效率,就好像是字典一样,如果要查“mysql”这个单词,我们肯定需要定位到m字母,然后从下往下找到y字母,再找到剩下的sql。如果没有索引,那么你可能需要 a - z,如果我想找到Java开头的单词呢?或者Oracle开头的单词呢?是不是觉得如果没有索引,这个事情根本无法完成?
我们可以简单理解为排好序的快速查找结构:
在数据之外,数据库系统还维护着满足特定查找算法的数据结构,这些数据结构以某种方式引用(指向)数据,这样就可以在这些数据结构上实现高级查找算法。这种数据结构,就是索引。下图就是一种可能的索引方式示例:
左边是数据表,一共有两列七条记录,Col1 是数据记录的物理地址,Col2 是记录的数据,为了加快Col2的查找,可以维护一个右边所示的二叉查找树,每个节点分别包含索引键值和一个指向对应数据记录物理地址的指针,这样就可以运用二叉查找在一定的复杂度内获取到相应数据,从而快速的检索出符合条件的记录。
一般来说索引本身也很大,不可能全部存储在内存中,因此索引往往以索引文件的形式存储的磁盘上.
我们平常所说的索引,如果没有特别指明,都是指B+树结构组织的索引。其中聚集索引,次要索引,覆盖索引,复合索引,前缀索引,唯一索引默认都是使用B+树索引,统称索引。当然,除了B+树这种类型的索引之外,还有哈稀索引(hash index)等。
优点
缺点
索引相关语句
创建:
CREATE [UNIQUE ] INDEX indexName ON mytable(columnname(length));
ALTER mytable ADD [UNIQUE ] INDEX [indexName] ON (columnname(length));
删除:
DROP INDEX [indexName] ON mytable;
查看:
SHOW INDEX FROM table_name\G;
使用 ALTER 命令:
-- 该语句添加一个主键,这意味着索引值必须是唯一的,且不能为NULL
ALTER TABLE tbl_name ADD PRIMARY KEY (column_list);
-- 这条语句创建索引的值必须是唯一的(除了NULL外,NULL可能会出现多次)
ALTER TABLE tbl_name ADD UNIQUE index_name (column_list);
-- 添加普通索引,索引值可出现多次
ALTER TABLE tbl_name ADD INDEX index_name (column_list);
-- 该语句指定了索引为 FULLTEXT ,用于全文索引
ALTER TABLE tbl_name ADD FULLTEXT index_name (column_list);
MySQL 的索引结构包含有:
BTree索引的检索原理
BTree索引的检索原理如下图所示:
【初始化介绍】
一颗b+树,浅蓝色的块我们称之为一个磁盘块,可以看到每个磁盘块包含几个数据项(深蓝色所示)和指针(黄色所示),如磁盘块1包含数据项17和35,包含指针P1、P2、P3。
P1表示小于17的磁盘块,P2表示在17和35之间的磁盘块,P3表示大于35的磁盘块。
真实的数据存在于叶子节点即3、5、9、10、13、15、28、29、36、60、75、79、90、99,非叶子节点只不存储真实的数据,只存储指引搜索方向的数据项,如17、35并不真实存在于数据表中。
【查找过程】
真实的情况是,3层的b+树可以表示上百万的数据,如果上百万的数据查找只需要三次IO,性能提高将是巨大的,如果没有索引,每个数据项都要发生一次IO,那么总共需要百万次的IO,显然成本非常非常高。
需要创建索引的情况
不需要创建索引的情况
Explain 即执行计划,使用 Explain 关键字可以模拟优化器执行SQL查询语句,从而知道MySQL是如何处理你的SQL语句的,分析你的查询语句或是表结构的性能瓶颈。
使用 Explain 我们可以知道:
Explain 的使用方式为:Explain + SQL语句,比如:
mysql> explain select * from edu_teacher where id='1297112152955490305';
+----+-------------+-------------+------------+-------+---------------+---------+---------+-------+------+----------+-------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------------+------------+-------+---------------+---------+---------+-------+------+----------+-------+
| 1 | SIMPLE | edu_teacher | NULL | const | PRIMARY | PRIMARY | 76 | const | 1 | 100.00 | NULL |
+----+-------------+-------------+------------+-------+---------------+---------+---------+-------+------+----------+-------+
1 row in set, 1 warning (0.02 sec)
下面我们解释一下 Explain 所包含各个字段的含义:
① id
select 查询的序列号,包含一组数字,表示查询中执行 select 子句或操作表的顺序。id 值包含一下三种情况:
② select_type
查询的类型,主要是用于区别普通查询、联合查询、子查询等的复杂查询:
③ table
显示这一行的数据是关于哪张表的
④ 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,则没有使用索引,查询中若使用了覆盖索引,则该索引仅出现在key列表中。
⑦ key_len
表示索引中使用的字节数,可通过该列计算查询中使用的索引的长度,在不损失精确性的情况下,长度越短越好。
key_len显示的值为索引字段的最大可能长度,并非实际使用长度,即 key_len 是根据表定义计算而得,不是通过表内检索出的。
⑧ ref
显示索引的哪一列被使用了,如果可能的话,是一个常数。哪些列或常量被用于查找索引列上的值。
⑨ rows
根据表统计信息及索引选用情况,大致估算出找到所需的记录所需要读取的行数
⑩ extra
包含不适合在其他列中显示但十分重要的额外信息:
重点理解覆盖索引:
覆盖索引(Covering Index),也可以称为索引覆盖,有一下两种理解方式:
注意:
如果要使用覆盖索引,一定要注意select列表中只取出需要的列,不可select *,因为如果将所有字段一起做索引会导致索引文件过大,查询性能下降。
Show Profile 是 MySQL 提供可以用来分析当前会话中语句执行的资源消耗情况,可以用于SQL的调优的测量,默认情况下,参数处于关闭状态,并保存最近15次的运行结果。
使用步骤:
看看当前的mysql版本是否支持:
mysql> show variables like 'profiling';
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| profiling | OFF |
+---------------+-------+
1 row in set, 1 warning (0.00 sec)
开启功能,默认是关闭,使用前需要开启:
mysql> set profiling=on;
Query OK, 0 rows affected, 1 warning (0.00 sec)
mysql> show variables like 'profiling';
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| profiling | ON |
+---------------+-------+
1 row in set, 1 warning (0.00 sec)
执行 SQL 语句
查看结果:
mysql> show profiles;
+----------+------------+---------------------------------+
| Query_ID | Duration | Query |
+----------+------------+---------------------------------+
| 1 | 0.00210675 | show variables like 'profiling' |
| 2 | 0.00018900 | show tables |
| 3 | 0.00018825 | SELECT DATABASE() |
| 4 | 0.00077650 | show tables |
| 5 | 0.00229300 | select * from acl_user |
+----------+------------+---------------------------------+
5 rows in set, 1 warning (0.00 sec)
诊断 SQL:show profile cpu, block io for query 上一步的Query_ID;
mysql> show profile cpu, block io for query 5;
+----------------------+----------+----------+------------+--------------+---------------+
| Status | Duration | CPU_user | CPU_system | Block_ops_in | Block_ops_out |
+----------------------+----------+----------+------------+--------------+---------------+
| starting | 0.000056 | 0.000000 | 0.000000 | NULL | NULL |
| checking permissions | 0.000006 | 0.000000 | 0.000000 | NULL | NULL |
| Opening tables | 0.001924 | 0.000000 | 0.000000 | NULL | NULL |
| init | 0.000018 | 0.000000 | 0.000000 | NULL | NULL |
| System lock | 0.000009 | 0.000000 | 0.000000 | NULL | NULL |
| optimizing | 0.000003 | 0.000000 | 0.000000 | NULL | NULL |
| statistics | 0.000012 | 0.000000 | 0.000000 | NULL | NULL |
| preparing | 0.000011 | 0.000000 | 0.000000 | NULL | NULL |
| executing | 0.000002 | 0.000000 | 0.000000 | NULL | NULL |
| Sending data | 0.000155 | 0.000000 | 0.000000 | NULL | NULL |
| end | 0.000004 | 0.000000 | 0.000000 | NULL | NULL |
| query end | 0.000007 | 0.000000 | 0.000000 | NULL | NULL |
| closing tables | 0.000007 | 0.000000 | 0.000000 | NULL | NULL |
| freeing items | 0.000066 | 0.000000 | 0.000000 | NULL | NULL |
| cleaning up | 0.000014 | 0.000000 | 0.000000 | NULL | NULL |
+----------------------+----------+----------+------------+--------------+---------------+
15 rows in set, 1 warning (0.02 sec)
日常开发需要注意的结论: