(译)MySQL 8.0实验室---MySQL中的倒叙索引(Descending Indexes)

译者注:
MySQL 8.0之前,不管是否指定索引建的排序方式,都会忽略创建索引时候指定的排序方式(语法上不会报错),最终都会创建为ASC方式的索引,
在执行查询的时候,只存在forwarded(正向)方式对索引进行扫描。
关于正向索引和反向索引,逻辑上很容易理解,这里有两个相关的概念:
正向索引或者反向索引,两者都是在构建B树索引时候的相关字段排序方式,是B索引树的逻辑存储方式
正向扫描(forward)和反向扫描( Backward index scan;)是执行查询的过程中对B树索引的扫描方式,是数据执行计划时候的一种索引扫描方式
关于正向扫描或者反向扫描不是随意的,受sql语句中(正/反向)排序方式以及(正/反向)索引的影响
之前在sqlserver中简单写过一点类似的东西,https://www.cnblogs.com/wy123/p/5552719.html

整体上看,抛开正向索引和反向索引,在扫描扫描的过程中,正向索引扫描的在性能上,稍微优于反向索引扫描。
不过,即便是反向索引扫描,也是优化器根据具体查询进行优化的结果,并非一个不好的选择。

以下为译文:



从8.0优化器实验室发布开始,MySQL开始支持倒叙索引。
正如我将在本文中详细介绍的,这个新特性可以用来消除对结果排序的需求,并在许多查询中带来性能改进。

简介

在此版本之前,所有索引都是按升序创建的。当语法本身被解析时,元数据不会被保留。例如在MySQL 5.7中:

mysql 5.7> CREATE TABLE t1 (a INT, b INT, INDEX a_desc_b_asc (a DESC, b ASC));
Query OK, 0 rows affected (0.47 sec)

mysql 5.7> SHOW CREATE TABLE t1\G
*************************** 1. row ***************************
       Table: t1
Create Table: CREATE TABLE `t1` (
  `a` int(11) DEFAULT NULL,
  `b` int(11) DEFAULT NULL,
  KEY `a_desc_b_asc` (`a`,`b`) <-- 创建索引时候的元数据没有被保留
) ENGINE=InnoDB DEFAULT CHARSET=latin1
1 row in set (0.00 sec)

应该注意的是,MySQL 5.7 optimizer能够向后扫描一个升序索引(按照降序排列),其成本较高。
如下面所示,我们可以看到正向索引扫描比反向索引扫描好~15%。
不能支持倒叙索引的主要限制是,优化器必须对混合顺序(如DESC、b ASC的顺序)使用文件排序。

MySQL 8.0中的改进

引入反向索引后,InnoDB现在可以按照降序顺序存储数据行,优化器将在查询中请求降序时利用它。
重复上面的例子,我们可以看到在创建表时索引顺序信息被正确地保留了:

mysql 8.0> CREATE TABLE t1 (a INT, b INT, INDEX a_desc_b_asc (a DESC, b ASC));
Query OK, 0 rows affected (0.47 sec)

mysql 8.0> show create table t1;
+-------+--------------------------------------------------------------------------------------------------------------------------------------------------------+
| Table | Create Table |
+-------+--------------------------------------------------------------------------------------------------------------------------------------------------------+
| t1 | CREATE TABLE `t1` (
`a` int(11) DEFAULT NULL,
`b` int(11) DEFAULT NULL,
KEY `a_desc_b_asc` (`a` DESC,`b`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 |
+-------+--------------------------------------------------------------------------------------------------------------------------------------------------------+
1 row in set (0.00 sec)

为了区分向后和向前索引扫描,还改进了EXPLAIN的输出。
对于MySQL-5.7,除了查询2和查询6之外,我们对所有查询都使用反向索引扫描或文件排序,因为这两个查询只需要升序。

Query 1: SELECT * FROM t1 ORDER BY a DESC;

mysql 8.0> explain SELECT * FROM t1 ORDER BY a DESC;
+----+-------------+-------+------------+-------+---------------+--------------+---------+------+------+----------+-------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+-------+---------------+--------------+---------+------+------+----------+-------------+
| 1  | SIMPLE    | t1    | NULL    | index  | NULL | a_desc_b_asc | 10 | NULL | 10 | 100.00 | Using index |
+----+-------------+-------+------------+-------+---------------+--------------+---------+------+------+----------+-------------+
1 row in set, 1 warning (0.00 sec)

Query 2: SELECT * FROM t1 ORDER BY a ASC;

mysql 8.0> explain SELECT * FROM t1 ORDER BY a ASC;
+----+-------------+-------+------------+-------+---------------+--------------+---------+------+------+----------+----------------------------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+-------+---------------+--------------+---------+------+------+----------+----------------------------------+
| 1 | SIMPLE | t1 | NULL | index | NULL | a_desc_b_asc | 10 | NULL | 10 | 100.00 | Backward index scan; Using index |
+----+-------------+-------+------------+-------+---------------+--------------+---------+------+------+----------+----------------------------------+
1 row in set, 1 warning (0.00 sec)

Query 3: SELECT * FROM t1 ORDER BY a DESC, b ASC;

mysql 8.0> EXPLAIN SELECT * FROM t1 ORDER BY a DESC, b ASC;
+----+-------------+-------+------------+-------+---------------+--------------+---------+------+------+----------+-------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+-------+---------------+--------------+---------+------+------+----------+-------------+
| 1 | SIMPLE | t1 | NULL | index | NULL | a_desc_b_asc | 10 | NULL | 10 | 100.00 | Using index |
+----+-------------+-------+------------+-------+---------------+--------------+---------+------+------+----------+-------------+
1 row in set, 1 warning (0.00 sec)

Query 4: SELECT * FROM t1 ORDER BY a ASC, b DESC;

mysql 8.0> EXPLAIN SELECT * FROM t1 ORDER BY a ASC, b DESC;
+----+-------------+-------+------------+-------+---------------+--------------+---------+------+------+----------+----------------------------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+-------+---------------+--------------+---------+------+------+----------+----------------------------------+
| 1 | SIMPLE | t1 | NULL | index | NULL | a_desc_b_asc | 10 | NULL | 10 | 100.00 | Backward index scan; Using index |
+----+-------------+-------+------------+-------+---------------+--------------+---------+------+------+----------+----------------------------------+
1 row in set, 1 warning (0.00 sec)

Query 5: SELECT * FROM t1 ORDER BY a DESC, b DESC;

mysql 8.0> EXPLAIN SELECT * FROM t1 ORDER BY a DESC, b DESC;
+----+-------------+-------+------------+-------+---------------+--------------+---------+------+------+----------+-----------------------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+-------+---------------+--------------+---------+------+------+----------+-----------------------------+
| 1 | SIMPLE | t1 | NULL | index | NULL | a_desc_b_asc | 10 | NULL | 10 | 100.00 | Using index; Using filesort |
+----+-------------+-------+------------+-------+---------------+--------------+---------+------+------+----------+-----------------------------+
1 row in set, 1 warning (0.01 sec)

Query 5: SELECT * FROM t1 ORDER BY a ASC, b ASC;

mysql 8.0> EXPLAIN SELECT * FROM t1 ORDER BY a ASC, b ASC;
+----+-------------+-------+------------+-------+---------------+--------------+---------+------+------+----------+-----------------------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+-------+---------------+--------------+---------+------+------+----------+-----------------------------+
| 1 | SIMPLE | t1 | NULL | index | NULL | a_desc_b_asc | 10 | NULL | 10 | 100.00 | Using index; Using filesort |
+----+-------------+-------+------------+-------+---------------+--------------+---------+------+------+----------+-----------------------------+
1 row in set, 1 warning (0.00 sec)

当表中有一个索引a_desc_b_asc (a DESC, b ASC)时,以下是上述6个查询的性能指标。

数据大小为1000万行。在MySQL-5.7中,它是a_asc_b_asc(a ASC, b ASC),因为不支持倒叙索引。

性能指标的解释:

1, 对于查询1,也即ORDER BY a DESC;:
我们看到查询1中性能的提升,因为请求的订单是“a”列的DESC
译者注:因为MySQL8.0中可以建立倒叙索引,查询1按照a字段desc排序,直接走正向(forwarded)索引扫描即可完成查询,
避免了在MySQL5.7中查询出来数据之后再进行排序操作的步骤

2,对于查询2:
由于查询2的排序为正序(译者注:与索引的顺序相反,因此需要反向扫描),由于反向索引扫描,
在MySQL-8.0中(相对于查询1)执行向反向索引扫描需要更多的时间
(注意,从图中可以看出,MySQL-8.0总体上表现更好。MySQL 5.7中正向索引扫描,与MySQL 8.0中反向索引扫描花费的时间(几乎)相同)

3,对于查询3 也即ORDER BY a DESC, b ASC;:
查询3的排序方式与查询1类似,然而在MySQL-5.7中,对于任何请求混合顺序的查询,会对查询结果重新排序,因此性能差别是巨大的。

4,对于查询4 也即 ORDER BY a ASC, b DESC;
可以看到,在MySQL 8.0中,查询4执行的是反向索引扫描,因此比查询3花费了更多的时间,
尽管如此,在查询5和查询6中,排序的方式是(a DESC, b DESC)/(a ASC, b ASC),不管是正向扫描还是反向扫描,都无法满足排序需求,因此会用到filesort
但是,在这种情况下,由于在MySQL-5.7中ASC/DESC索引标志被忽略(译者注:MySQL 5.7中没有正向和反向索引的概念),因此MySQL-5.7可以使用(正向/反向)索引扫描来给出请求的顺序。

5,如果用户想要避免查询5和查询6的filesorts,可以修改表以添加一个键(a ASC, b ASC)。
此外,如果用户也想避免反向索引扫描,可以同时添加(a ASC, b DESC)和(a DESC, b DESC)。

下面是添加了第5点下的额外索引后的MySQL-5.7.14和MySQL-8.0-labs的最后对比:

注意,在MySQL-5.7中,我们不能添加额外的索引来提高上述查询的性能。
而且,有了这个特性,在某些情况下可以避免物化,比如在连接中的第一个表上请求混合顺序。
在一些用例中,反向索引提高了性能。区间扫描访问方法也使用反向索引。
虽然并不是所有的范围扫描访问方法都使用反向索引,但我们将在未来尝试消除这些限制。

改进

随着降序索引的引入,我们已经删除了对隐式排序的支持,结果是作为GROUP BY的一部分提到的列的升序。
除了上述改进外,我们还看到在一些情况下性能得到了改善,这些情况下的顺序是隐含的,但可能不是必需的。

总结

我们很高兴能够解决MySQL社区长期存在的功能请求之一。请了解反向索引的特性,让我们知道你的想法!

原文地址:https://www.cnblogs.com/wy123/p/9512561.html

时间: 2024-08-29 11:42:01

(译)MySQL 8.0实验室---MySQL中的倒叙索引(Descending Indexes)的相关文章

MySQL 8.0 中统计信息直方图的尝试

直方图是表上某个字段在按照一定百分比和规律采样后的数据分布的一种描述,最重要的作用之一就是根据查询条件,预估符合条件的数据量,为sql执行计划的生成提供重要的依据. 在MySQL 8.0之前的版本中,MySQL仅有一个简单的统计信息却没有直方图,没有直方图的统计信息可以说是没有任何意义的. MySQL 8.0新特性之一就是开始支持统计信息的直方图,这个概念很早就提出来了,抽空具体尝试了一下使用方法. 之前写过MSSQL相关统计信息的一点东西,在原理上都是一致的, 照旧,直接上例子,造数据,创建一

mysql 8.0.11 中使用 grant ... identified by 时 error 1064 near &#39;identified by &#39;密码&#39;&#39; at line 1

看一下问题图片: 1 问题: 当使用 grant 权限列表 on 数据库 to '用户名'@'访问主机' identified by '密码'; 时会出现"......near 'identified by '密码'' at line 1"这个错误 2 原因: 因为新版的的mysql版本已经将创建账户和赋予权限的方式分开了 3解决办法: 创建账户:create user '用户名'@'访问主机' identified by '密码'; 赋予权限:grant 权限列表 on 数据库 to

Atitit.mysql&#160;5.0&#160;5.5&#160;&#160;5.6&#160;5.7&#160;&#160;新特性&#160;新功能

Atitit.mysql 5.0 5.5  5.6 5.7  新特性 新功能 1. MySQL  5.6    5 大新特性1 1.1. 优化器的改进1 1.2. InnoDB 改进1 1.3. 使用 memcached API 直接访问 NoSQL2 1.4. 更好的复制2 1.5. Performance Schema2 2. MySQL 5.7.62 2.1. 内建中文全文索引2 2.2. 多主复制2 2.3. other2 3. 参考2 1. MySQL  5.6    5 大新特性 M

Atitit.mysql 5.0 5.5 &#160;5.6 5.7 &#160;新特性 新功能

Atitit.mysql 5.0 5.5  5.6 5.7  新特性 新功能 1. MySQL  5.6    5 大新特性1 1.1. 优化器的改进1 1.2. InnoDB 改进1 1.3. 使用 memcached API 直接访问 NoSQL2 1.4. 更好的复制2 1.5. Performance Schema2 2. MySQL 5.7.62 2.1. 内建中文全文索引2 2.2. 多主复制2 2.3. other2 3. 参考2 1. MySQL  5.6    5 大新特性 M

zabbix3.0 监控mysql服务免用户名密码登录的问题故障处理详细过程

1,My.cnf中用户名密码无效 在azure云上面,使用Zabbix监控mysql中,发现在/usr/local/mysql/my.cnf里面设置的默认用户名密码无效,出不来数据,而且在zabbix服务器上,使用zabbix_get也报错failed,如下 [[email protected]_serv_121_12 ~]#/usr/local/zabbix/bin/zabbix_get -s 192.168.13.13 -p10050 -kmysql.status[Uptime] /usr/

MySQL &#183; 8.0版本更新 &#183; 性能优化篇

摘要: 本文主要总结下MySQL在8.0版本和性能相关的一些改动,随着新的小版本的发布,本文将不断进行更新,直到正式GA. 已更新版本MySQL 8.0.0MySQL 8.0.0 WL#9387: InnoDB: Group purging of rows by table ID 这个问题最早是faceb... 本文主要总结下MySQL在8.0版本和性能相关的一些改动,随着新的小版本的发布,本文将不断进行更新,直到正式GA. MySQL 8.0.0 WL#9387: InnoDB: Group

Qt5.5.0使用mysql编写小软件源码讲解---顾客信息登记表

一个个人觉得比较简单小巧的软件. 下面就如何编写如何发布打包来介绍一下吧! 先下载mysql的库文件链接:http://files.cnblogs.com/files/xiaobo-Linux/mysql.zip 把两个文件放入 Qt目录\Qt5.5.0\5.5\mingw492_32\bin文件夹下直接粘贴(路径根据自己的设置) 也可以放到原来工程代码中,头文件用双引号引出即可. <!--小波Linux QQ463431476 cnblogs http://www.cnblogs.com/xi

RedHat下apache\ftp\mysql 4.0 的安装方法

RedHat下安装这三个服务的方法大同小异 Apache服务: 找到Apache安装包: rpm -ivh httpd-2.0.40-21.i386.rpm 等待安装完成即可 检查安装结果: rpm -qa|grep httpd 有回显httpd-2.0.40-21即表示安装成功. 启动apache服务: service httpd start 通过浏览器访问 IP地址80端口 即可打开apache测试网站 注意事项: 1.apache 服务无法启动,主机名与IP不匹配 解决方法: 手动修改主机

mysql导入数据到oracle中

mysql导入数据到oracle中. 建立Oracle表: CREATE TABLE "GG_USER" ( "USERID" VARCHAR2(255 BYTE) NOT NULL, "ISPART" VARCHAR2(255 BYTE) DEFAULT NULL, "ACTUALPOSITIONID" NUMBER(11) DEFAULT NULL, "BEGINWORKTIME" TIMESTAMP(