什么是降序索引?
我们都知道,索引是有序的;不过,在MySQL之前版本中,只支持升序索引,不支持降序索引,这会带来一些问题;在最新的MySQL 8.0版本中,引入了降序索引,接下来我们来学习一下。
限制条件
- MySQL 8.0版本开始才真正支持降序索引(Descending Index)
- 只有Innodb存储引擎支持降序索引
- 只支持B-Tree降序索引
- MySQL 8.0不再对GROUP BY操作进行隐式排序
降序索引的使用
创建测试表:
MySQL5.7
mysql> create table t5(id int(10) auto_increment primary key,name varchar(40),age int(4),index idx_name_age(name asc,age desc));
Query OK, 0 rows affected (0.23 sec)
MySQL8.0
mysql> create table t5(id int(10) auto_increment primary key,name varchar(40),age int(4),index idx_name_age(name asc,age desc));
Query OK, 0 rows affected, 2 warnings (0.19 sec)
可以看到:从语法上来说,MySQL5.7和MySQL8.0都支持降序索引的语法。
查看表结构:
MySQL5.7
mysql> show create table t5 \G
*************************** 1. row ***************************
Table: t5
Create Table: CREATE TABLE `t5` (
`id` int(10) NOT NULL AUTO_INCREMENT,
`name` varchar(40) DEFAULT NULL,
`age` int(4) DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `idx_name_age` (`name`,`age`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4
1 row in set (0.00 sec)
MySQL8.0
mysql> show create table t5 \G
*************************** 1. row ***************************
Table: t5
Create Table: CREATE TABLE `t5` (
`id` int NOT NULL AUTO_INCREMENT,
`name` varchar(40) COLLATE utf8mb4_general_ci DEFAULT NULL,
`age` int DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `idx_name_age` (`name`,`age` DESC)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci
1 row in set (0.00 sec)
查表结构发现:MySQL5.7并不支持降序索引,而MySQL8.0支持。
插入测试数据:
MySQL5.7
mysql> insert into t5(name,age) values('dongchen',32);
Query OK, 1 row affected (0.06 sec)
mysql> insert into t5(name,age) values('liudi',29);
Query OK, 1 row affected (0.04 sec)
mysql> insert into t5(name,age) values('chenxu',25);
Query OK, 1 row affected (0.04 sec)
mysql> insert into t5(name,age) values('kaite',35);
Query OK, 1 row affected (0.05 sec)
mysql> insert into t5(name,age) values('steven',33);
Query OK, 1 row affected (0.04 sec)
MySQL8.0
mysql> insert into t5(name,age) values('dongchen',32);
Query OK, 1 row affected (0.05 sec)
mysql> insert into t5(name,age) values('liudi',29);
Query OK, 1 row affected (0.01 sec)
mysql> insert into t5(name,age) values('chenxu',25);
Query OK, 1 row affected (0.00 sec)
mysql> insert into t5(name,age) values('kaite',35);
Query OK, 1 row affected (0.00 sec)
mysql> insert into t5(name,age) values('steven',33);
Query OK, 1 row affected (0.00 sec)
查看执行计划:
MySQL5.7
mysql> explain select * from t5 order by name asc,age desc \G
*************************** 1. row ***************************
id: 1
select_type: SIMPLE
table: t5
partitions: NULL
type: index
possible_keys: NULL
key: idx_name_age
key_len: 168
ref: NULL
rows: 5
filtered: 100.00
Extra: Using index; Using filesort
1 row in set, 1 warning (0.00 sec)
MySQL8.0
mysql> explain select * from t5 order by name asc,age desc \G
*************************** 1. row ***************************
id: 1
select_type: SIMPLE
table: t5
partitions: NULL
type: index
possible_keys: NULL
key: idx_name_age
key_len: 168
ref: NULL
rows: 5
filtered: 100.00
Extra: Using index
1 row in set, 1 warning (0.00 sec)
可以看到:按照name字段升序,age字段降序排序,MySQL8.0 age字段排序可以用到索引,而MySQL5.7 age字段用不到索引,Extra为Using filesort。 接下来,我们继续看执行计划: MySQL5.7
mysql> explain select * from t5 order by name desc,age \G
*************************** 1. row ***************************
id: 1
select_type: SIMPLE
table: t5
partitions: NULL
type: index
possible_keys: NULL
key: idx_name_age
key_len: 168
ref: NULL
rows: 5
filtered: 100.00
Extra: Using index; Using filesort
1 row in set, 1 warning (0.00 sec)
MySQL8.0
mysql> explain select * from t5 order by name desc,age \G
*************************** 1. row ***************************
id: 1
select_type: SIMPLE
table: t5
partitions: NULL
type: index
possible_keys: NULL
key: idx_name_age
key_len: 168
ref: NULL
rows: 5
filtered: 100.00
Extra: Backward index scan; Using index
1 row in set, 1 warning (0.00 sec)
可以看到:按照name降序,age升序,MySQL8.0依然可以用到联合索引,使用了索引的反向扫描。
查看GROUP BY隐式排序:
MySQL5.7
mysql> select count(*),age from t5 group by age;
+----------+------+
| count(*) | age |
+----------+------+
| 1 | 25 |
| 1 | 29 |
| 1 | 32 |
| 1 | 33 |
| 1 | 35 |
+----------+------+
5 rows in set (0.00 sec)
MySQL8.0
mysql> select count(*),age from t5 group by age;
+----------+------+
| count(*) | age |
+----------+------+
| 1 | 25 |
| 1 | 32 |
| 1 | 35 |
| 1 | 29 |
| 1 | 33 |
+----------+------+
5 rows in set (0.00 sec)
可以看到,MySQL5.7 在group by中对分组字段进行了隐式排序,而MySQL8.0取消了隐式排序。