1. 首页
  2. >
  3. 数据库技术
  4. >
  5. MySQL

mysql索引优化实战一

CREATE TABLE `eb_special_subject` (   `id` int NOT NULL AUTO_INCREMENT,   `name` varchar(255) NOT NULL DEFAULT '' COMMENT '名称',   `pic` varchar(255) NOT NULL DEFAULT '' COMMENT '图标',   `sort` int NOT NULL DEFAULT '0' COMMENT '排序',   `is_show` int NOT NULL DEFAULT '0' COMMENT '是否显示,1=显示,0=隐藏',   `grade_id` int NOT NULL DEFAULT '0' COMMENT '年级部id',   `add_time` int NOT NULL DEFAULT '0' COMMENT '添加时间',   `is_del` tinyint(1) NOT NULL DEFAULT '0' COMMENT '删除',   PRIMARY KEY (`id`),   KEY `name` (`name`,`pic`) USING BTREE ) ENGINE=InnoDB AUTO_INCREMENT=32 DEFAULT CHARSET=utf8 COMMENT='科目列表';  INSERT INTO `test`.`eb_special_subject`(`id`, `name`, `pic`, `sort`, `is_show`, `grade_id`, `add_time`, `is_del`) VALUES (21, '语文', 'http://testtest.oss-cn-beijing.aliyuncs.com/67c04202002111757584709.jpg', 10, 1, 4, 1581401495, 0); INSERT INTO `test`.`eb_special_subject`(`id`, `name`, `pic`, `sort`, `is_show`, `grade_id`, `add_time`, `is_del`) VALUES (22, '数学', 'http://testtest.oss-cn-beijing.aliyuncs.com/8b0be202002111757582367.jpg', 9, 1, 4, 1581415097, 0); INSERT INTO `test`.`eb_special_subject`(`id`, `name`, `pic`, `sort`, `is_show`, `grade_id`, `add_time`, `is_del`) VALUES (23, '英语', 'http://testtest.oss-cn-beijing.aliyuncs.com/5ae6f202002111757583267.jpg', 8, 1, 3, 1581415148, 0); INSERT INTO `test`.`eb_special_subject`(`id`, `name`, `pic`, `sort`, `is_show`, `grade_id`, `add_time`, `is_del`) VALUES (24, '音乐启蒙', 'http://testtest.oss-cn-beijing.aliyuncs.com/67c04202002111757584709.jpg', 0, 1, 4, 1581476723, 0); INSERT INTO `test`.`eb_special_subject`(`id`, `name`, `pic`, `sort`, `is_show`, `grade_id`, `add_time`, `is_del`) VALUES (25, '体育训练', 'http://testtest.oss-cn-beijing.aliyuncs.com/5ae6f202002111757583267.jpg', 0, 1, 4, 1581476740, 0); INSERT INTO `test`.`eb_special_subject`(`id`, `name`, `pic`, `sort`, `is_show`, `grade_id`, `add_time`, `is_del`) VALUES (26, '课外辅导', 'http://testtest.oss-cn-beijing.aliyuncs.com/8b0be202002111757582367.jpg', 0, 1, 4, 1581476755, 0); INSERT INTO `test`.`eb_special_subject`(`id`, `name`, `pic`, `sort`, `is_show`, `grade_id`, `add_time`, `is_del`) VALUES (27, '野外拓展', 'http://testtest.oss-cn-beijing.aliyuncs.com/67c04202002111757584709.jpg', 0, 1, 4, 1581476777, 0); INSERT INTO `test`.`eb_special_subject`(`id`, `name`, `pic`, `sort`, `is_show`, `grade_id`, `add_time`, `is_del`) VALUES (28, '精品提升', 'http://testtest.oss-cn-beijing.aliyuncs.com/5ae6f202002111757583267.jpg', 0, 1, 4, 1581476806, 0); INSERT INTO `test`.`eb_special_subject`(`id`, `name`, `pic`, `sort`, `is_show`, `grade_id`, `add_time`, `is_del`) VALUES (30, '二级测试', 'http://testtest.oss-cn-beijing.aliyuncs.com/ccb10202002111757423753.jpg', 0, 1, 10, 1581674369, 0); INSERT INTO `test`.`eb_special_subject`(`id`, `name`, `pic`, `sort`, `is_show`, `grade_id`, `add_time`, `is_del`) VALUES (31, '学习专栏', 'http://cremb-zsff.oss-cn-beijing.aliyuncs.com/68244202002171745063469.jpg', 1, 1, 11, 1607856852, 0); 

上面是示例代码,创建一个test数据库,创建表和新增数据。

Mysql如何选择合适的索引

mysql>EXPLAIN SELECT * from eb_special_subject where name >'a';


mysql索引优化实战一


如果用name索引需要遍历name字段联合索引树,然后还需要根据遍历出来的主键值去主键索引树里再去查出最终数据,成本比全表扫描还高,可以用覆盖索引优化,这样只需要遍历name字段的联合索引树就能拿到所有结果,如下:

mysql> EXPLAIN SELECT name from eb_special_subject where name >'a';


mysql索引优化实战一


对于上面 name>'a' 的执行结果,mysql最终是否选择走索引或者一张表涉及多个索引,mysql最终如何选择索引,我们可以用trace工具来一查究竟,开启trace工具会影响mysql性能,所以只能临时分析sql使用,用完之后立即关闭.

常见SQL深入优化

order by 与group by优化

1、MySQL支持两种方式的排序filesort和index,Using index是指MySQL扫描索引本身完成排序。index效率高,filesort效率低。

2、order by满足两种情况会使用Using index。

1) order by语句使用索引最左前列。

2) 使用where子句与order by子句条件列组合满足索引最左前列。

3、尽量在索引列上完成排序,遵循索引建立(索引创建的顺序)时的最左前缀法则。

4、如果order by的条件不在索引列上,就会产生Using filesort。

5、能用覆盖索引尽量用覆盖索引

6、group by与order by很类似,其实质是先排序后分组,遵照索引创建顺序的最左前缀法则。对于group by的优化如果不需要排序的可以加上order by null禁止排序。

注意,where高于having,能写在where中的限定条件就不要去having限定了。

Using filesort文件排序原理详解

filesort文件排序方式

  • 单路排序:是一次性取出满足条件行的所有字段,然后在sort buffer中进行排序;用trace工具可以看到sort_mode信息里显示< sort_key, additional_fields >或者< sort_key,packed_additional_fields >
  • 双路排序(又叫回表排序模式):是首先根据相应的条件取出相应的排序字段和可以直接定位行数据的行 ID,然后在 sort buffer 中进行排序,排序完后需要再次取回其它需要的字段;用trace工具可以看到sort_mode信息里显示< sort_key, rowid >MySQL 通过比较系统变量 max_length_for_sort_data(默认1024字节) 的大小和需要查询的字段总大小来判断使用哪种排序模式。

如果 max_length_for_sort_data 比查询字段的总长度大,那么使用 单路排序模式;

如果 max_length_for_sort_data 比查询字段的总长度小,那么使用 双路排序模式。

我们先看单路排序的详细过程:

1. 从索引name找到第一个满足 name = ‘cww’ 条件的主键 id

2. 根据主键 id 取出整行,取出所有字段的值,存入 sort_buffer 中

3. 从索引name找到下一个满足 name = ‘cww’ 条件的主键 id

4. 重复步骤 2、3 直到不满足 name = ‘cww’

5. 对 sort_buffer 中的数据按照字段 position 进行排序

6. 返回结果给客户端

我们再看下双路排序的详细过程:

1. 从索引 name 找到第一个满足 name = ‘cww’ 的主键id

2. 根据主键 id 取出整行,把排序字段 position 和主键 id 这两个字段放到 sort buffer 中

3. 从索引 name 取下一个满足 name = ‘cww’ 记录的主键 id

4. 重复 3、4 直到不满足 name = ‘cww’

5. 对 sort_buffer 中的字段 position 和主键 id 按照字段 position 进行排序

6. 遍历排序好的 id 和字段 position,按照 id 的值回到原表中取出 所有字段的值返回给客户端

其实对比两个排序模式,单路排序会把所有需要查询的字段都放到 sort buffer 中,而双路排序只会把主键和需要排序的字段放到 sort buffer 中进行排序,然后再通过主键回到原表查询需要的字段。

如果 MySQL 排序内存配置的比较小并且没有条件继续增加了,可以适当把 max_length_for_sort_data 配置小点,让优化器选择使用双路排序算法,可以在sort_buffer 中一次排序更多的行,只是需要再根据主键回到原表取数据。

如果 MySQL 排序内存有条件可以配置比较大,可以适当增大 max_length_for_sort_data 的值,让优化器优先选择全字段排序(单路排序),把需要的字段放到 sort_buffer 中,这样排序后就会直接从内存里返回查询结果了。

所以,MySQL通过 max_length_for_sort_data 这个参数来控制排序,在不同场景使用不同的排序模式,从而提升排序效率。

注意,如果全部使用sort_buffer内存排序一般情况下效率会高于磁盘文件排序,但不能因为这个就随便增大sort_buffer(默认1M),mysql很多参数设置都是做过优化的,不要轻易调整。