2056728.jpg

什么是降序索引?

我们都知道,索引是有序的;不过,在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取消了隐式排序。