mysql索引优化实战

MySQL 索引优化全攻略
Mysql最左匹配原则
MYSQL | 最左匹配原则

索引类型

        在实际应用中,InnoDB MySQL 建表时默认的存储引擎, B+Tree 索引类型也是 MySQL 存储引擎采用最多的索引类型。
        在创建表时,InnoDB 存储引擎默认使用表的主键作为主键索引,该主键索引就是聚簇索引( Clustered Index),如果表没有定义主键, InnoDB 就自己产生一个隐藏的 6 个字节的主键 ID 值作为主键索引, 而创建的主键索引默认使用的是 B+Tree 索引

聚集索引:
一个表中只能有一个,聚集索引的顺序与数据真实的物理存储顺序一致。查询速度贼快,聚集索引的叶子节点上是该行的所有数据 ,数据索引能加快范围查询(聚集索引的顺序和数据存放的逻辑顺序一致)。主键!=聚集索引。

辅助索引(非聚集索引):
一个表中可以有多个,叶子节点存放的不是一整行数据,而是键值,叶子节点的索引行中还包含了一个'书签',这个书签就是指向聚簇索引的一个指针,从而在聚簇索引树中找到一整行数据。


聚集索引与辅助索引的区别:
叶子节点是否存放的为一整行数据

执行计划 Explain

id 执行顺序

select_type 查询类型,主要是用于区分 普通查询、联合查询、子查询

table 用到的表
type 访问类型 system > const > eq_ref > ref > range > index > ALL 至少  range
key 前查询语句真实使用的索引名称 , 个字段为 null ,一个是当前表中没有索引 二是当前表有索引 但是失效了

possible_keys 指出MySQL能使用哪个索引在表中找到记录,查询涉及到的字段上若存在索引,则该索引将被列出,但不一定被查询使用(该查询可以利用的索引,如果没有任何索引显示 null)

rows 扫描的行数
filtered 返回结果的行数占需读取行数的百分比, filtered 的值越大越好, 100 是最好的情况。 `
Extra 记录了十分重要的额外信息,这些额外信息有:
        Using filesort: mysql对数据使用一个外部的文件内容进行了排序
        Using temporary: 使用临时表保存中间结果,也就是说mysql在对查询结果排序时使用了临时表,比如在order by 或 group by 中间MySql处理过程需要多处理一个临时表,一般这种情况是需要优化处理的。
        Using index: 表示相应的select操作中使用了覆盖索引(Covering Index),避免了访问表的数据行,效率高,Using index不读数据文件,只从索引文件获取数据
        Using where:使用了 where过滤条件,过滤元组和是否读取数据文件或索引文件没有关系
        Using index condition:索引下推
        

1.测试联合索引的最左原则的时候, 发现了5.6版本后的新特性Index Condition Pushdown

2.含义就是存储引擎层根据索引尽可能的过滤数据,然后在返回给服务器层根据where其他条件进行过滤

        
• EXPLAIN不会告诉你关于触发器、存储过程的信息或用户自定义函数对查询的影响情况
• EXPLAIN不考虑各种Cache
• EXPLAIN不能显示MySQL在执行查询时所作的优化工作
• 部分统计信息是估算的,并非精确值
• EXPALIN只能解释SELECT操作,其他操作要重写为SELECT后查看执行计划。

创建索引的技巧

  1. 维度高的列创建索引。
    • 数据列中不重复值出现的个数,这个数量越高,维度就越高。
    • 如数据表中存在8行数据a,b ,c,d,a,b,c,d这个表的维度为4。
    • 要为维度高的列创建索引,如性别和年龄,那年龄的维度就高于性别。
    • 性别这样的列不适合创建索引,因为维度过低。
  2. 对 where,on,group by,order by 中出现的列使用索引。
  3. 对较小的数据列使用索引,这样会使索引文件更小,同时内存中也可以装载更多的索引键。
  4. 为较长的字符串使用前缀索引。
  5. 不要过多创建索引,除了增加额外的磁盘空间外,对于DML操作的速度影响很大,因为其每增删改一次就得从新建立索引。
  6. 使用组合索引,可以减少文件索引大小,在使用时速度要优于多个单列索引。

表结构

CREATE TABLE `bank` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `total_number` int(11) DEFAULT NULL,
  `total_name` varchar(80) DEFAULT NULL,
  `branch_number` varchar(16) DEFAULT NULL,
  `branch_name` varchar(80) DEFAULT NULL,
  `city_name` int(11) DEFAULT NULL,
  `branch_bank_name` varchar(200) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=142101 DEFAULT CHARSET=utf8mb4;

测试sql导入

链接:https://pan.baidu.com/s/1YsOMYDpNv8u-dMh-5xxGLg 
提取码:asdf

唯一索引

查询语句 编号唯一的场景

EXPLAIN SELECT * from bank WHERE branch_number = 103673767189;

执行计划

如何优化

添加branch_number为唯一索引

ALTER TABLE `bank` ADD UNIQUE ( `branch_number`);

mysql索引优化实战_第1张图片

 添加的唯一索引为什么没起作用呢?

 索引字段类型不匹配

 `branch_number` varchar(16) DEFAULT NULL

  branch_number 是 varchar 类型,查询语句branch_number 条件为 int 类型所以索引失效

# 修改后查询语句
EXPLAIN SELECT * from bank WHERE branch_number = '103673767189';

mysql索引优化实战_第2张图片

联合索引

为什么要使用联合索引

多个单列索引在多条件查询时只会生效第一个索引!所以多条件联合查询时最好建联合索引!

1、减少开销。建一个联合索引(col1,col2,col3),实际相当于建了(col1),(col1,col2),(col1,col2,col3)三个索引。每多一个索引,都会增加写操作的开销和磁盘空间的开销。对于大量数据的表,使用联合索引会大大的减少开销!

2、覆盖索引。对联合索引(col1,col2,col3),如果有如下的sql: select col1,col2,col3 from test where col1=1 and col2=2。那么MySQL可以直接通过遍历索引取得数据,而无需回表,这减少了很多的随机io操作。减少io操作,特别的随机io其实是dba主要的优化策略。所以,在真正的实际应用中,覆盖索引是主要的提升性能的优化手段之一。

3、效率高。索引列越多,通过索引筛选出的数据越少。有1000W条数据的表,有如下sql:select from table where col1=1 and col2=2 and col3=3,假设假设每个条件可以筛选出10%的数据,如果只有单值索引,那么通过该索引能筛选出1000W10%=100w条数据,然后再回表从100w条数据中找到符合col2=2 and col3= 3的数据,然后再排序,再分页;如果是联合索引,通过索引筛选出1000w10% 10% *10%=1w,效率提升可想而知!

   

注意:在建立联合索引时,如何安排索引内的字段顺序?

1.因为最左匹配原则,建立(a,b)索引 则不需要在单独建立(a)索引了,所以通过调整顺序少维护一个索引,那么这个顺序就是优先考虑的

2.考虑索引的占用空间问题,a字段比b字段大时,建议创建(a,b)索引,这样后面需要单独使用b作为索引时能节约空间

3.筛选性大的优先排在前面

EXPLAIN SELECT branch_name from bank 
WHERE total_name = '中国银行' AND city_name = 5510

mysql索引优化实战_第3张图片

# 创建联合索引
ALTER TABLE bank ADD INDEX index_name ( total_name, city_name );

mysql索引优化实战_第4张图片

注意:由于查询的数据 branch_name 只在主键索引上有,所以不得不回表查询,回到主键索引数搜索的我们称为回表。

覆盖索引

 简单来说,覆盖索引包含所有需要查询的数据列。

 注:遇到以下情况,执行计划不会选择覆盖查询。

  1. select选择的字段中含有不在 索引 中的字段 ,即索引没有覆盖全部的列。
  2. where条件中不能含有对索引进行like的操作。

由于覆盖索引可以减少树的搜索次数,显著提升查询性能,所以使用覆盖索引是一个常用的性能优化手段

# 删除索引
ALTER TABLE `bank` DROP INDEX `index_name` ;
# 再次创建索引 包含 branch_name ,则为覆盖索引,无需回表查询 
ALTER TABLE bank ADD INDEX index_name ( total_name, city_name,branch_name);

mysql索引优化实战_第5张图片

# total_number未在索引中,所以覆盖索引失效
EXPLAIN SELECT total_number,branch_name FROM bank 
WHERE total_name = '中国银行' AND city_name = 5510

mysql索引优化实战_第6张图片

最左匹配原则

联合索引存在最左匹配原则

最左前缀原则:

顾名思义是最左优先,以最左边的为起点任何连续的索引都能匹配上, 
注:如果第一个字段是范围查询需要单独建一个索引 
注:在创建联合索引时,要根据业务需求,where子句中使用最频繁的一列放在最左边。这样的话扩展性较好,比如 userid 经常需要作为查询条件,而 mobile 不常常用,则需要把 userid 放在联合索引的第一位置,即最左边

同时存在联合索引和单列索引(字段有重复的),这个时候查询mysql会怎么用索引呢?

这个涉及到mysql本身的查询优化器策略了,当一个表有多条索引可走时, Mysql 根据查询语句的成本来选择走哪条索引;

有人说where查询是按照从左到右的顺序,所以筛选力度大的条件尽量放前面。网上百度过,很多都是这种说法,但是据我研究,mysql执行优化器会对其进行优化,当不考虑索引时,where条件顺序对效率没有影响,真正有影响的是是否用到了索引!

 MYSQL | 最左匹配原则

 index_name ( total_name, city_name,branch_name)

# 走索引
EXPLAIN SELECT * from bank WHERE total_name = '中国银行' AND  city_name = 5510; 
EXPLAIN SELECT * from bank WHERE city_name = 5510  AND total_name = '中国银行'; 
EXPLAIN SELECT * from bank WHERE total_name = '中国银行' AND branch_name = "中国进出口银行湖南省分行"; 

# 没走索引
EXPLAIN SELECT * from bank WHERE city_name = 5510; 
EXPLAIN SELECT * from bank WHERE city_name = 5510 AND branch_name = "中国进出口银行湖南省分行"; 
# 走索引
EXPLAIN SELECT * from bank WHERE total_name LIKE '招商%'; 

# 不走索引
EXPLAIN SELECT * from bank WHERE total_name LIKE '%招商'; 
EXPLAIN SELECT * from bank WHERE total_name LIKE '%招商%'; 

前缀索引

怎么给字符串字段加索引?

# branch_bank_name 字段没加索引时的查询语句
EXPLAIN SELECT * from bank WHERE branch_bank_name LIKE '广安市%'; 

mysql索引优化实战_第7张图片

# 为branch_bank_name字段添加索引
ALTER TABLE bank ADD INDEX index_branch_bank_name (branch_bank_name);
EXPLAIN SELECT * from bank WHERE branch_bank_name LIKE '广安市%'; 

mysql索引优化实战_第8张图片

使用前缀索引

ALTER TABLE bank DROP INDEX `index_branch_bank_name`; 
# 前缀索引,只取前6个字段作为索引
ALTER TABLE bank ADD INDEX index_branch_bank_name (branch_bank_name(6));

mysql索引优化实战_第9张图片

使用前缀索引,定义好长度,就可以大大节约索引的占用空间,又不会额外的增加太多的查询成本

索引下推

mysql 5.6 引入了索引下推优化(Index Condition Pushdown),可以在索引遍历过程对包含的字段先做判断直接过滤不符合条件的记录,以减少回表的次数

mysql索引优化实战_第10张图片

索引的CURD

# 1.添加PRIMARY KEY(主键索引) 
mysql>ALTER TABLE `table_name` ADD PRIMARY KEY ( `column` ) 
# 2.添加UNIQUE(唯一索引) 
mysql>ALTER TABLE `table_name` ADD UNIQUE ( `column`) 
# 3.添加INDEX(普通索引) 
mysql>ALTER TABLE `table_name` ADD INDEX index_name ( `column` ) 
# 4.添加FULLTEXT(全文索引) 
mysql>ALTER TABLE `table_name` ADD FULLTEXT ( `column`) 
# 5.添加多列索引 
mysql>ALTER TABLE `table_name` ADD INDEX index_name ( `column1`, `column2`, `column3` )

# 更新索引 删除索引后重新创建
# 这两句都是等价的,都是删除掉table_name中的索引index_name; 
DROP INDEX `index_name` ON `talbe_name`  
ALTER TABLE `table_name` DROP INDEX `index_name` 

# 删除主键索引,注意主键索引只能用这种方式删除
ALTER TABLE `table_name` DROP PRIMARY KEY 

什么样的sql不走索引

-- 不会使用索引,因为所有索引列参与了计算 
SELECT `sname` FROM `stu` WHERE `age`+10=30;

-- 不会使用索引,因为使用了函数运算,原理与上面相同 
SELECT `sname` FROM `stu` WHERE LEFT(`date`,4) <1990; 


-- 走索引 
SELECT * FROM `houdunwang` WHERE `uname` LIKE'后盾%' 

SELECT * FROM `houdunwang` WHERE `uname` LIKE "%后盾%" -- 不走索引 

-- 正则表达式不使用索引,这应该很好理解,所以为什么在SQL中很难看到regexp关键字的原因 

-- 字符串与数字比较不使用索引; 
CREATE TABLE `a` (`a` char(10)); 
EXPLAIN SELECT * FROM `a` WHERE `a`="1" -- 走索引 
EXPLAIN SELECT * FROM `a` WHERE `a`=1 -- 不走索引 

-- 如果条件中有or,即使其中有条件带索引也不会使用。
-- 换言之,就是要求使用的所有字段,都必须建立索引,我们建议大家尽量避免使用or关键字 
select * from dept where dname='xxx' or loc='xx' or deptno=45 

-- 如果mysql估计使用全表扫描要比使用索引快,则不使用索引

oracle查询不走索引的一些状况(索引失效)

你可能感兴趣的:(mysql,mysql,数据库,索引)