Mysql-03索引最佳实践

示例表 记录数100000+
CREATE TABLE `employees` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(24) NOT NULL DEFAULT '' COMMENT '姓名',
  `age` int(11) NOT NULL DEFAULT '0' COMMENT '年龄',
  `position` varchar(20) NOT NULL DEFAULT '' COMMENT '职位',
  `hire_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '入职时间',
  PRIMARY KEY (`id`),
  KEY `idx_name_age_position` (`name`,`age`,`position`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8 COMMENT='员工记录表';

1.联合索引第一个字段使用范围不会走索引 

EXPLAIN SELECT * FROM employees WHERE name > 'LiLei' AND age = 22 AND position ='manager';

2. 联合索引第一个字段使用范围,force index强制走索引

    扫描的行数rows变少了,但是需要回表

 EXPLAIN SELECT * FROM employees force index(idx_name_age_position) WHERE name > 'LiLei' AND age = 22 AND position ='manager';

-- 关闭查询缓存
set global query_cache_size=0;  
set global query_cache_type=0;
-- 执行时间0.121s
SELECT * FROM employees WHERE name > 'LiLei';
-- 执行时间0.239s
SELECT * FROM employees force index(idx_name_age_position) WHERE name > 'LiLei';

3.覆盖索引优化 

 EXPLAIN SELECT name,age,position FROM employees WHERE name > 'LiLei' AND age = 22 AND position ='manager';

4.in和or在数据量大的时候会走索引,数据量小的时候会选择全表扫描(mysql8成本计算做了改动,数据量小的时候也会走索引) 

 EXPLAIN SELECT * FROM employees WHERE name in ('LiLei','HanMeimei','Lucy') AND age = 22 AND position ='manager';

EXPLAIN SELECT * FROM employees WHERE (name = 'LiLei' or name = 'HanMeimei') AND age = 22 AND position ='manager';

EXPLAIN SELECT * FROM employees_copy WHERE name in ('LiLei','HanMeimei','Lucy') AND age = 22 AND position ='manager';

EXPLAIN SELECT * FROM employees_copy WHERE (name = 'LiLei' or name = 'HanMeimei') AND age = 22 AND position ='manager';

5. 模糊查询kk%一般会走索引

 EXPLAIN SELECT * FROM employees WHERE name like 'LiLei%' AND age = 22 AND position ='manager';

6. 什么是索引下推? 

Index Condition Pushdown,简单来说比如如下sql:

EXPLAIN SELECT * FROM employees WHERE name like 'LiLei%' AND age = 22 AND position ='manager';

联合索引(name,age,position)通常只会走name字段索引(name过滤后age和position是无序的),拿到符合name条件的主键id后回表

如果使用索引下推,联合索引(name,age,position)在索引遍历过程中,对索引包含的所有字段先做判断,过滤掉不符合条件的记录再回表,这样可以大大减少回表的次数

如果范围条件过滤后结果集较小会走索引下推,结果集较大不会走索引下推

7. mysql如何选择合适的索引?

 EXPLAIN select * from employees where name > 'a' ;

 EXPLAIN select * from employees where name > 'zzz' ;

mysql是否选择走索引或者一张表有多个索引mysql最终选择走哪个索引,可以用trace工具来查看详情,trace工具会影响mysql性能,所以只能临时分析sql使用,用完之后立即关闭

set session optimizer_trace="enabled=on",end_markers_in_json="on";  --开启trace
set session optimizer_trace="enabled=off";    --关闭trace

select * from employees where name > 'a' order by position;
SELECT * FROM information_schema.OPTIMIZER_TRACE;

Mysql-03索引最佳实践_第1张图片

Mysql-03索引最佳实践_第2张图片

Mysql-03索引最佳实践_第3张图片

可以看出mysql计算出全表扫描的成本低于使用索引的成本,最终选择全表扫描

select * from employees where name > 'zzz' order by position;
SELECT * FROM information_schema.OPTIMIZER_TRACE;

查看trace字段可知索引扫描的成本低于全表扫描,所以mysql最终选择索引扫描

8.索引设计的原则总结

1)代码先行,索引后上

主题业务功能开发完毕,把所以涉及到的相关sql都拿出来分析之后再建立索引

2)联合索引尽量覆盖条件

设计几个联合索引,尽量覆盖sql里的where、order by、group by字段,保证联合索引字段顺序满足sql查询的最左前缀原则,尽量少建单值索引

3)不要在小基数字段上建立索引

基数较小的列比如性别字段只有男女,表记录多时无法利用B+树二分查找的效率优势

4)长字符串可以采用前缀索引

尽量在字段类型较小的列建立索引,因为占用空间小,搜索性能也会高

在varchar(255)这样的字段建立索引,可以优化一下建立类似KEY index(name(20),age,position)这样的前缀索引,根据where name字段搜索时先在索引树里根据name的前20个字符搜索,定位到前20个字符匹配的数据之后再回到聚簇索引提取出完整的name字段进行比对。

索引树里name字段只包含前20个字符所以时无序的,order by name和group by name无法用到name索引

5)where和order by冲突是优先where

where条件基于索引先筛选出数据,再对筛选出的数据排序成本会小很多(因为排序数据少)

6)慢查询sql做优化

监控后台的一些慢sql,针对慢sql查询做特定的索引优化,后续我会专门针对慢sql优化做总结。

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