MySQL 索引管理及执行计划

一、mysql索引类型
  • BTREE:B+树索引 (常用)

  • HASH:HASH索引

  • FULLTEXT:全文索引

  • RTREE:R树索引

二、索引分类
  • 主键索引

  • 普通索引*

  • 唯一索引

三、索引基本管理
?
创建和删除:
alter table stu add index idx_name(stu_name); # 创建索引
alter table stu drop index idx_name; # 删除索引
?
或者
?
create index inx_name on stu(stu_name);
drop index inx_name on stu;
?
查询索引设置
desc stu;
四、MySQL中的约束索引

?

==主键索引==
  • 只能有一个主键。

  • 索引:列的内容是唯一值,高中学号.

  • 建的时候至少要有一个主键索引,最好和业务无关

走主键索引的查询效率是最高的,我们尽量每个表有一个主键,并且将来查询的时候计量以主键为条件查询

  1. 建表时创建推荐

CREATE TABLE `test` (
`id` INT(4) NOT NULL AUTO_INCREMENT,
`name` CHAR(20) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=INNODB DEFAULT CHARSET=UTF8;
  1. 建立表后增加

CREATE TABLE `test1` (
`id` INT(4) NOT NULL,
`name` CHAR(20) NOT NULL
) ENGINE=INNODB DEFAULT CHARSET=UTF8; # 简历一个test1表
?
ALTER TABLE test1 CHANGE id id INT(4) PRIMARY KEY NOT NULL AUTO_INCREMENT; # 增加自增主键
==普通索引==
  • 加快查询速度,工作中优化数据库的关键。

  • 在合适的列上建立索引,让数据查询更高效。

create index index_name on test(name);
alter table test add index index_name(name);
  • 用了索引,查一堆内容。

  • 在where条件关键字后面的列建立索引才会加快查询速度.

select id,name from test where state=1 order by id group by name;

?

==前缀索引:根据字段的前N个字符建立索引==

?

create index index_name on test(name(8));
?
==联合索引:多个字段建立一个索引==

特点:前缀生效特性。

原则:把最常用来作为条件查询的列放在前面。

alter table stu add index minx(gender,age); # 查询时优先匹配gender
==前缀加联合索引==
create index index_name on test(name(8),sex(2));
?
==唯一性索引==
create unique index index_name on test(name);
五、企业sql优化思路

1、把一个大的不使用索引的SQL语句按照功能进行拆分

2、长的SQL语句无法使用索引,能不能变成2条短的SQL语句让它分别使用上索引。

3、对SQL语句功能的拆分和修改

4、减少“烂”SQL 由运维(DBA)和开发交流(确认),共同确定如何改,最终由DBA执行

5、制定开发流程

不适合走索引的场景

1、唯一值少的列上不适合建立索引或者建立索引效率低。例如:性别列

  • 查看表的唯一值数量:

select count(distinct user) from mysql.user;  # user列不重复的有几行
select count(distinct user,host) from mysql.user; #user,host不重复的有几行
SELECT COUNT(*) FROM mysql.user; #一共有多少行

2、小表可以不建立索引,100条记录。

3、对于数据仓库,大量全表扫描的情况,建索引反而会慢

?

六、explain执行计划

?

explain(执行计划),使用explain关键字可以模拟优化器执行sql查询语句,从而知道MySQL是如何处理sql语句。explain主要用于分析查询语句或表结构的性能瓶颈

?

mysql> explain select name,gender,age from test where gender=‘F‘ and age <20;
+----+-------------+-------+-------+---------------+----------+---------+------+------+-----------------------+
| id | select_type | table | type | possible_keys | key ? ? | key_len | ref | rows | Extra ? ? ? ? ? ? ? ? |
+----+-------------+-------+-------+---------------+----------+---------+------+------+-----------------------+
| 1 | SIMPLE ? ? | test | range | inx_test ? ? | inx_test | 7 ? ? ? | NULL | ? 1 | Using index condition |
?
?

6.1、explain 个列的解释

  • table:显示这一行的数据是关于哪张表的。

  • type:这是重要的列,显示连接使用了何种类型。从最好到最差的连接类型为 const、eq_reg、ref、range、index 和 ALL。

  • possible_keys:显示可能应用在这张表中的索引。如果为空,没有可能的索引。可以为相关的域从 WHERE 语句中选择一个合适的语句。

  • key: 实际使用的索引。如果为 NULL,则没有使用索引。很少的情况下,MYSQL 会选择优化不足的索引。这种情况下,可以在 SELECT 语句中使用 USE INDEX(indexname)来强制使用一个索引或者用 IGNORE INDEX(indexname)来强制 MYSQL 忽略索引。

  • key_len:使用的索引的长度。在不损失精确性的情况下,长度越短越好。如果太长建议把普通的索引替换成前缀索引

  • ref:显示索引的哪一列被使用了,如果可能的话,是一个常数。

  • rows:根据表统计信息及索引选用情况,大致估算出找到所需的记录所需要读取的行数,也就是说,用的越少越好

  • Extra:关于MYSQL如何解析查询的额外信息。将在表4.3中讨论,但这里可以看到的坏的例子是Using temporary和Using filesort,意思MYSQL根本不能使用索引,结果是检索会很慢

?

6.2、extra 列返回的描述的意义

1、Distinct : 一旦 MySQL 找到了与行相联合匹配的行,就不再搜索了。

2、Range checked for each Record(index map:#): 没有找到理想的索引,因此对于从前面表中来的每一个行组合,MySQL 检查使用哪个索引,并用它来从表中返回行。这是使用索引的最慢的连接之一。

3、Using filesort : 看到这个的时候,查询就需要优化了。MySQL 需要进行额外的步骤来发现如何对返回的行排序。它根据连接类型以及存储排序键值和匹配条件的全部行的行指针来排序全部行。

4、Using index : 列数据是从仅仅使用了索引中的信息而没有读取实际的行动的表返回的,这发生在对表的全部的请求列都是同一个索引的部分的时候。

5、Using temporary 看到这个的时候,查询需要优化了。这里,MYSQL 需要创建一个临时表来存储结果,这通常发生在对不同的列集进行 ORDER BY 上,而不是 GROUP BY 上 Where used 使用了 WHERE 从句来限制哪些行将与下一张表匹配或者是返回给用户。如果不想返回表中的全部行,并且连接类型 ALL或 index,这就会发生,或者是查询有问题不同连接类型的解释(按照效率高低的顺序排序)。

6、system 表只有一行:system表。这是const连接类型的特殊情况

7、const : 表中的一个记录的最大值能够匹配这个查询(索引可以是主键或惟一索引)。因为只有一行,这个值实际就是常数,因为 MySQL 先读这个值然后把它当做常数来对待。

8、eq_ref : 在连接中,MySQL 在查询时,从前面的表中,对每一个记录的联合都从表中读取一个记录,它在查询使用了索引为主键或惟一键的全部时使用。

9、ref : 这个连接类型只有在查询使用了不是惟一或主键的键或者是这些类型的部分(比如,利用最左边前缀)时发生。对于之前的表的每一个行联合,全部记录都将从表中读出。这个类型严重依赖于根据索引匹配的记录多少—越少越好。

10、range:这个连接类型使用索引返回一个范围中的行,比如使用>或<查找东西时发生的情况

11、index: 这个连接类型对前面的表中的每一个记录联合进行完全扫描(比 ALL 更好,因为索引一般小于表数据)。

12、ALL : 这个连接类型对于前面的每一个记录联合进行完全扫描,这一般比较糟糕,应该尽量避免

13、Not exists : MySQL 优化了LEFT JOIN,一旦它找到了匹配 LEFT JOIN 标准的行,就不再搜索了。

==出现Using temporary ,Using filesort ,Using join buffer 一般是排序 order by ,group by ,distinct,条件上没有索引例如: explain select * from city where countrycode=‘CHN‘ order by population; 或者在join 的条件列上没有建立索引==

==type : 表示MySQL在表中找到所需行的方式,又称“访问类型”,==

  • 常见类型如下:ALL,index, range, ref, eq_ref, const, system, NULL从左到右,性能从最差到最好

  1. ==ALL:==

Full Table Scan, MySQL将遍历全表以找到匹配的行

如果显示ALL,说明: 查询没有走索引:说明一语句本身的问题、二、索引的问题,没建立索引

例子

explain select count(*) from stu ;
  1. ==index==

index: Full Index Scan,Index与All区别为index类型只遍历索引树。这通常比ALL快,因为索引文件通常比数据文件小。(也就是说虽然all和Index都是读全表,但index是从索引中读取的,而all是从硬盘读取的) 例子:

?

?

  1. ==range==

range:索引范围扫描,对索引的扫描开始于某一点,返回匹配值域的行。 显 而易见的索引范围扫描是带有between或者where子句里带有<,>查询。 where 条件中有范围查询或模糊查询时如:

>  < >= <= ? between  and ? in () ? or
like ‘xx%‘

当mysql使用索引去查找一系列值时,例如IN()和OR列表,也会显示range(范围扫描),当然性能上面是有差异的。

?

  1. ==ref==

对于普通索引这就是最高的级别了 ref:使用非唯一索引扫描或者唯一索引的前缀扫描,返回==匹配某个单独值的记录行==比如:

where stu_name=‘xiaoming‘
?
explain select * from stu where stu_name=‘aa‘;

5、 ==eq_ref==

eq_ref:类似ref,区别就在使用的索引是唯一索引,对于每个索引键值,表中只有一条记录匹配,简单来说,就是==多表连接中使用primary key或者 unique key作为关联条件==

join条件使用的是primary key或者 unique key

?

6、 ==const,system==

const、system:当MySQL对查询某部分进行优化,并转换为一个常量时,使用这些类型访问。 如==将主键置于where条件的条件==,MySQL就能将该查询转换为一个常量

explain select * from city where id=1;

  1. ==null==

NULL:MySQL在优化过程中分解语句,执行时甚至不用访问表或索引, 例如走子查询的数据在父查询中有,又或者数据在内存中直接有

6.3 不走索引的情况

  • 没有查询条件,或者查询条件没有建立索引

select * from tab; 全表扫描。 select * from tab where 1=1;
  • 在业务数据库中,特别是数据量比较大的表。 是没有全表扫描这种需求。

1、对用户查看是非常痛苦的。

2、对服务器来讲毁灭性的。

(1)select * from tab;
?
SQL改写成以下语句: selec * from tab order by price limit 10 需要在price列上建立索引
?
(2) select * from tab where name=‘zhangsan‘ name列没有索引
?
1、换成有索引的列作为查询条件
?
2、将name列建立索引
  • 查询结果集是原表中的大部分数据,应该是30%以上。

查询的结果集,超过了总数行数30%,优化器觉得就没有必要走索引了。

假如:tab表 id,name id:1-100w ,id列有索引
?
select * from tab where id>500000;
?
如果业务允许,可以使用limit控制。
怎么改写 ? 结合业务判断,有没有更好的方式。如果没有更好的改写方案 尽量不要在mysql存放这个数据了。放到redis里面。
  • 索引本身失效,统计数据不真实

索引有自我维护的能力。 对于表内容变化比较频繁的情况下,有可能会出现索引失效。

  • 查询条件使用函数在索引列上,或者对索引列进行运算,运算包括(+,-,*,/,! 等)

例子: 错误的例子:select * from test where id-1=9; 正确的例子:select * from test where id=10;
  • 隐式转换导致索引失效.这一点应当引起重视.也是开发中经常会犯的错误. 由于表的字段tu_mdn定义为varchar2(20),但在查询时把该字段作为number类型以where条件传给数据库, 这样会导致索引失效.

     错误的例子:select * from test where tu_mdn=13333333333; 正确的例子: select * from test where tu_mdn=‘13333333333‘;
  • <> ,not in 不走索引

EXPLAIN SELECT * FROM teltab WHERE telnum ? <> ‘110‘;
EXPLAIN SELECT * FROM teltab WHERE telnum NOT IN (‘110‘,‘119‘);
------------
mysql> select * from tab where telnum <> ‘1555555‘;
+------+------+---------+
| id ? | name | telnum |
+------+------+---------+
| ? 1 | a ? | 1333333 |
+------+------+---------+
1 row in set (0.00 sec)
?
mysql> explain select * from tab where telnum <> ‘1555555‘;

单独的>,<,in 有可能走,也有可能不走,和结果集有关,尽量结合业务添加limit or或in 尽量改成union

EXPLAIN SELECT * FROM teltab WHERE telnum IN (‘110‘,‘119‘); 改写成:

EXPLAIN SELECT * FROM teltab WHERE telnum=‘110‘ UNION ALL SELECT * FROM teltab WHERE telnum=‘119‘


  • like "%_" 百分号在最前面不走

EXPLAIN SELECT * FROM teltab WHERE telnum LIKE ‘31%‘ 走range索引扫描
?
EXPLAIN SELECT * FROM teltab WHERE telnum LIKE ‘%110‘ 不走索引
?
%linux%类的搜索需求,可以使用elasticsearch
?
上一篇:数据库:数据库的创建,删除 备份恢复,数据库的常用数据类型,数据库表的修改


下一篇:全量同步多张db表到ES同一个索引