生也有涯,知也无涯。这篇文章主要讲述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中的约束索引
==主键索引==:
- 只能有一个主键。
- 索引:列的内容是唯一值,高中学号.
- 建的时候至少要有一个主键索引,最好和业务无关
- 【MySQL 索引管理及执行计划】建表时创建推荐
CREATE TABLE `test` (
`id` INT(4) NOT NULL AUTO_INCREMENT,
`name` CHAR(20) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=INNODB DEFAULT CHARSET=UTF8;
- 建立表后增加
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从左到右,性能从最差到最好
- ==ALL:==
例子
explain select count(*) from stu ;
- ==index==
- ==range==
> < > = < =betweenandin ()or
like \'xx%\'
- ==ref==
where stu_name=\'xiaoming\'
?
explain select *from stuwhere 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;
- ==null==
6.3 不走索引的情况
- 没有查询条件,或者查询条件没有建立索引
select * from tab; 全表扫描。 select * from tab where 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%以上。
假如: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\';
EXPLAINSELECT * FROM teltab WHERE telnumNOT 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
?
推荐阅读
- InnoDB 表空间
- 浅入浅出 MySQL 索引
- ArcGIS10.7连接Windows下HGDB5.6.5
- 探究Presto SQL引擎-巧用Antlr
- sqlserver登录名及角色权限的那些事
- 简单了解 MySQL 中相关的锁
- 超硬核学习手册系列3事务视图篇——深入浅出MySQL的知识点,学习收藏必备
- 前端 JavaScript 实现一个简易计算器
- 万字长文(SpringCloud gateway入门学习&实践)