使用Order By时能否通过索引排序?
我们知道在很多场景下会导致索引失效,比如说没有遵循B+树的最左匹配原则,但是也有一些情况是遵循了最左匹配原则但是还是没有走索引,这里我们使用order by进行排序的时候就有不走索引的情况,那么带大家来分析一下
drop table if exists `user`;
drop table if exists `user_example`;
create table `user`(
`id` int primary key comment '主键ID',
`card_id` int comment '身份证',
`nickname` varchar(10) comment '昵称',
`age` int not null comment '年龄',
key `card_id` (`card_id`)
) engine=InnoDB default charset=utf8mb4;
// 这里我们明明对card_id建好了单列索引,那为什么不走索引呢?
select * from `user` order by card_id
- 如果索引覆盖是可以走索引的
- 如果带上索引条件是可以走索引的
通过索引排序内部流程是什么呢?
explain select nickname,card_id,age from user order by card_id;
我们在了解MySQL
底层是怎么排序的之前,我们先来了解一下一个概念 sort buffer
.
首先mysql
会为每一个线程都分配一个固定大小的sort buffer
用于排序。它是一个具有逻辑概念的内存区域,我们可以通过sort_buffer_size
参数来控制,默认值是256kb
。
// 输入查看最,小可以设置为 32K,最大可以设置为 4G。
show variables like 'sort_buffer_size';
由于sort buffer
大小是一个固定的,但是我们待排序的数据量它不是,所以根据它们之间的一个差值呢,就分为了内部排序和外部排序
- 当待排序的数据量小于等于
sort buffer
时,那我们的sort buffer就能够容纳,MySQL就可以直接在内存里面排序就行了,内部排序使用的排序算法是快排
- 当待排序的数据量大于
sort buffer
时,那我们的sort buffer
就不够用了对吧。这个时候MySQL就得要借助外部文件来进行排序了。将待排序数据拆成多个小文件,对各个小文件进行排序,最后再汇总成一个有序的文件,外部排序使用的算法时归并排序
我们来聊聊row_id排序
和大家说一个这个参数max_length_for_sort_data
,在我们MySQL中专门控制用户排序的行数据长度参数。默认是4096
,也就是说如果超过了这个长度MySQL就会自动升级成row_id
算法。
// 默认max_length_for_sort_data的大小为4096字节
show variables like 'max_length_for_sort_data';
row_id
排序的思想就是把不需要的数据不放到sort_buffer
中,让sort_buffer
中只存放需要排序的字段。
举个例子:
explain select nickname,card_id,age from user order by card_id;
我们前面说到了sort buffer
,在sort buffer
里面进行排序的数据是我们select的全部字段,所以当我们查询的字段越多,那么sort buffer
能容纳的数据量也就越小。而通过row_id排序就只会存放row_id 字段和排序相关的字段。其余的字段等排序完成之后通过主键ID进行回表拿。
group by 分组和 order by 在索引使用上有什么不同吗?
没什么太大的差异group by
实际是先进行排序,再进行分组。所以遵循order by的索引机制。