0
点赞
收藏
分享

微信扫一扫

04-MySql 索引失效情况和优化

you的日常 2022-01-18 阅读 53

准备数据

CREATE TABLE `student` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `stuno` int(11) NOT NULL,
  `name` varchar(20) DEFAULT NULL,
  `age` int(3) DEFAULT NULL,
  `classId` int(11) DEFAULT NULL,
  PRIMARY KEY (`id`),
  KEY `idx_age_classId_name` (`age`,`classId`,`name`)
) ENGINE=InnoDB AUTO_INCREMENT=500001 DEFAULT CHARSET=utf8;

索引失效案例

单标索引失效案例

最左前缀匹配

范围查询索引右边的列失效

-- 因为索引classId 使用了范围查找所以 索引右边的列失效了, 右边的数据name没有使用到索引, 左边的两个列还是使用到的
EXPLAIN SELECT SQL_NO_CACHE * FROM student 
WHERE student.age=30 AND student.classId>20 AND student.name = 'abc' ; 

不等于(!= 或者<>)索引失效

以% 开头的无法使用索引, 以% 结尾的可以使用

OR 前后存在非索引的列,索引失效

关联查询子查询

  • 设置joinBuffer

内连接

Left Join

子查询

排序优化

  • 提高 sort_buffer_size
#创建索引  
CREATE  INDEX idx_age_classid_name ON student (age,classid,NAME);
-- 没有加limit 不走索引
EXPLAIN  SELECT SQL_NO_CACHE * FROM student ORDER BY age,classid; 
-- 走索引
EXPLAIN  SELECT SQL_NO_CACHE * FROM student ORDER BY age,classid LIMIT 10;  
-- 没有回表查询走索引
EXPLAIN  SELECT SQL_NO_CACHE id,age,classid,name FROM student ORDER BY age,classid; 
-- 没有回表走索引
EXPLAIN  SELECT SQL_NO_CACHE id,age FROM student ORDER BY age asc,classid desc LIMIT 10;  
-- 降序走索引
EXPLAIN  SELECT SQL_NO_CACHE * FROM student ORDER BY age desc,classid desc LIMIT 10;  
-- 不走索引
EXPLAIN  SELECT SQL_NO_CACHE * FROM student ORDER BY age asc,classid desc LIMIT 10;  



Group By 优化

  • 几乎和 order by 一样 都能够使用到所有 提高 sort buffer 的大小

limit 优化

-- 这是一个全表扫描没法使用到索引 得一条一条遍历拿到100000 条后往后数10条
EXPLAIN SELECT SQL_NO_CACHE * FROM student  LIMIT 100000,10;

-- 优化思路使用主键排序
EXPLAIN  SELECT SQL_NO_CACHE s2.* FROM student s1 
INNER JOIN (select  id from student ORDER BY id LIMIT 1000,10) s2
  on s1.id=s2.id;

优先使用覆盖索引

CREATE INDEX idx_age_name ON student (age,NAME);
show index FROM student;
-- 不等于不使用索引
EXPLAIN SELECT * FROM student WHERE age <> 20;
-- 基于成本考虑在二级索引里面就包含这两个字段和主键, 所以就使用了二级索引
EXPLAIN SELECT age,NAME FROM student WHERE age <> 20;

前缀索引

 alter table teacher add index index2(email(6));
  • 如果我们使用了 前缀索引就无法使用覆盖索引了, 应为列数据不全必须进行回表查询
  • 使用Hash 函数对长度较高的字符串进行构建索引比较好SELECT CRC32('hello'), 这样存储也有一个弊端就是要存储再加上一个字段

索引下推

CREATE  INDEX idx_age_name ON student (age,name); 
-- 使用到了索引下推 excra= Using index condition
EXPLAIN  SELECT * FROM student WHERE age=45 ORDER BY name
-- sql 的执行步骤, 二级索引先找到 age=45的, 二级索引直接执行 order by name 语句 应为他已经排好序了, 回表查询的时候就无需排序了

-- 例子二
EXPLAIN  SELECT * FROM student WHERE age=45 and  name like '%aa'

  • ICP的使用条件:
    ① 只能用于二级索引(secondary index)
    ②explain显示的执行计划中type值(join 类型)为 range 、 ref 、 eq_ref 或者 ref_or_null 。
    ③ 并非全部where条件都可以用ICP筛选,如果where条件的字段不在索引列中,还是要读取整表的记录
    到server端做where过滤。
    ④ ICP可以用于MyISAM和InnnoDB存储引擎
    ⑤ MySQL 5.6版本的不支持分区表的ICP功能,5.7版本的开始支持。
    当SQL使用覆盖索引时,不支持ICP优化方法

普通索引和 唯一索引的对比

其他优化策略

EXISTS 和 IN的那个效率高

-- 如果主表比较大 子查询表比较小使用in
-- 是不相关的子查询, 先查询子查询 然后固定值查询 主表
EXPLAIN  SELECT id FROM student WHERE id in(select age from student WHERE age = 48)
-- 主查询表比较小 子查询表比较大, 所以用这种比较好
-- 相关子查询, 查询主表 主表那一条数据送到子查询去查询
EXPLAIN  SELECT s.id FROM student s WHERE EXISTS(select * FROM student ss WHERE s.id=ss.id)

count(*) ,count(1) 那个效率高

  • 首先不考虑非空等音素 count(*) 和 count(1) 执行效率是一样的
  • MyISAM 存储引擎 使用表级锁, 底层记录了一个常量是很快的 o (1) 复杂度;
  • InnoDB 存储引擎使用行级锁 所以就需要一条一条去累加了, count(*) 和 count(1) 效率一样, 但是不建议使用主键? 为啥呢, 主要是主键它下面存储是数据节点 那么都需要吧数据加载到内存 所以效率低, 使用具体的某个非空的字段可以选择;
    count(*) 和count(1) 底层自动选择一个占用小的二级索引进行统计 , 也就是 key_len
-- 可以执行以下sql 明显走非主键索引了
EXPLAIN  SELECT SQL_NO_CACHE COUNT(*) FROM student;
EXPLAIN  SELECT SQL_NO_CACHE COUNT(1) FROM student; 

select(*) 性能影响

limit 1 对优化的影响

举报

相关推荐

0 条评论