mysql 深入学习三 索引优化一

测试建表

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=4 DEFAULT CHARSET=utf8 COMMENT='员工记录表';
 
INSERT INTO employees(name,age,position,hire_time) VALUES('LiLei',22,'manager',NOW());
INSERT INTO employees(name,age,position,hire_time) VALUES('HanMeimei', 23,'dev',NOW());
INSERT INTO employees(name,age,position,hire_time) VALUES('Lucy',23,'dev',NOW());

注意: 联合索引:KEY `idx_name_age_position` (`name`,`age`,`position`) USING BTREE

全值匹配

  1.联合索引中只利用‘name’字段索引: key_len = ‘name’字段长度 ( 存汉字则长度是 3n + 2 字节)  3*24+2

  2.联合索引中利用 ‘name’ 和 ‘age’ 字段索引: key_len = ‘name’字段长度 + 'age'字段长度(int:4字节)

  3.联合索引中全部利用 ‘name’ 、‘age’和 ‘position’ 字段索引 : key_len = ‘name’字段长度 + 'age'字段长度+‘position’字段长度 (( 存汉字则长度是 3n + 2 字节)  3n+2)3*20+2

    注意:修改索引字段顺序,mysql会优化成定义的联合索引的顺序,尽管不影响使用,但还是建议使用定义的联合索引的顺序

最左前缀法则

  如果索引了多列,要遵守最左前缀法则。指的是查询从索引的最左前列开始并且不跳过索引中的列。
  错误示例一:跳过了age索引,只有name索引生效,position索引不生效  

   错误示例二:未使用name索引,导致其他两个索引失效,整体未使用索引

   错误示例二:未使用 ‘name’  和 ‘age’ 索引,导致 'position' 索引 失效,整体未使用索引

不在索引列上做任何操作(计算、函数、(自动or手动)类型转换),会导致索引失效而转向全表扫描

   1.此处在 ‘name’索引字段上使用了 left()函数操作,导致不走索引:因为left()函数运算后的值在索引树上不一定能找得到值

   2.测试前执行:ALTER TABLE `employees` ADD INDEX `idx_hire_time` (`hire_time`) USING BTREE ; #给hire_time增加一个普通索引:

    此处在 ‘hire_time’索引字段上使用了 date()函数操作,导致不走索引

  

    可以优化为日期范围查询,有可能会走索引:注意此处key列也有可能为Null,不走索引,这里mysql底层会根据评估值来进行选择是否走索引,这里《抛出问题一

     测试后执行:ALTER TABLE `employees` DROP INDEX `idx_hire_time`; #删除hire_time的普通索引

存储引擎不能使用索引中范围条件右边的列 

  注意key_len 为78,说明只走了 'name' 和 ‘age’ 索引,是因为 ‘age’ 索引是范围索引,不能保证 ‘position’ 索引的无序性,所以 ‘position’ 索引未生效

尽量使用覆盖索引(只访问索引的查询(索引列包含查询列)),减少 select * 语句 。(覆盖索引不需要回表查询,只需要在索引表就完成查询了)

   使用覆盖索引

   未使用覆盖索引

mysql在使用不等于(!=或者<>),not in ,not exists 的时候无法使用索引会导致全表扫描

< 小于、 > 大于、 <=、>= 这些,mysql内部优化器会根据检索比例、表大小等多个因素整体评估是否使用索引

is null,is not null 一般情况下也无法使用索引

like以通配符开头('%abc...')mysql索引失效会变成全表扫描操作 

  注意:‘%Lei’ 没走索引

   注意,‘Lei%’ 走了索引 。区别left(name, 3)的函数情况,因为explain没有对函数做底层优化!毕竟explain并不是真的执行sql语句,只是分析

   解决一:那么如何优化 ‘%Lei’ 这种情况呢? 使用覆盖索引

  解决二:如果不能使用覆盖索引则可能需要借助搜索引擎 

字符串不加单引号索引失效 

 

少用or或in,用它查询时,mysql不一定使用索引,mysql内部优化器会根据检索比例、表大小等多个因素整体评估是否使用索引,详见范围查询优化

范围查询优化 

  测试前添加:ALTER TABLE `employees` ADD INDEX `idx_age` (`age`) USING BTREE ; #age字段添加索引

  没走索引原因:mysql内部优化器会根据检索比例、表大小等多个因素整体评估是否使用索引。比如这个例子,可能是由于单次数据量查询过大导致优化器最终选择不走索引
  优化方法:可以将大的范围拆分成多个小范围(注意:多大范围内使用索引是由mysql来评估的,是否跟表字段最大值有关?)

   测试后添加:ALTER TABLE `employees` DROP INDEX `idx_age`; #age字段删除索引

 

-- mysql5.7关闭ONLY_FULL_GROUP_BY报错
select version(), @@sql_mode;SET sql_mode=(SELECT REPLACE(@@sql_mode,'ONLY_FULL_GROUP_BY',''));
  

 

posted @ 2023-08-26 17:23  花开如梦  阅读(36)  评论(0编辑  收藏  举报