MySQL InnoDB 二级索引的排序示例详解

作者:coderbee笔记 时间:2024-01-15 17:11:53 

排序问题

最近看了极客时间上 《MySQL实战45讲》,纠正了一直以来对 InnoDB 二级索引的一个理解不到位,正好把相关内容总结下。

PS:本文的所有测试基于 MySQL 8.0.13 。

先把问题抛出来,下面的 SQL 所创建的表,有两个查询语句,哪个索引是非必须的?


CREATE TABLE `geek` (
`a` int(11) NOT NULL,
`b` int(11) NOT NULL,
`c` int(11) NOT NULL,
`d` int(11) NOT NULL,
PRIMARY KEY (`a`,`b`),
KEY `c` (`c`),
KEY `ca` (`c`,`a`),
KEY `cb` (`c`,`b`)
) ENGINE=InnoDB;

select * from geek where c=N order by a limit 1;
select * from geek where c=N order by b limit 1;

作者给的答案是索引 c 和 ca 的数据模型是一样的,因此 ca 是多余的。为啥??

我们知道,二级索引里存放的不是行的位置,而是主键的值,也知道索引是有序的。

如果 c 与 ca 的数据模型一样,那么就要求二级索引的叶子节点不仅是按索引列排序、而且还按关联的主键值进行排序。

我以前的理解是 二级索引只按索引列进行排序,主键值是不排序的。

问了专栏作者,得到的答复是:索引 c 就是按照 cab 这样排序,(二级索引))有保证主键算进去、还是有序的。(PS:非原话,前后问了三次得到)。

本着 先问是不是,再问为什么 的思路,进行一番探究。

是不是?

如果能直接看 InnoDB 的数据文件,那就可以直接看出是不是遵循了这样的排序规则。可惜那是二进制文件,又没有顺手的工具可以方便查看,放弃。

后来找到了 MySQL 的 handler 语句,它支持 MyISAM/InnoDB 两种引擎的表。handler 语句提供了直接访问表存储引擎的接口。

下面的语法表示读取指定表指定索引的 第一条/前一条/下一条/最后一条 记录。


handler table_name/table_name_alias read index_name first/pre/next/last;

就用 handler 语句来验证下,先建一个简单的表,插入几条数据:


create table t_simple (
id int primary key,
v int,
key k_v (v)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

insert into t_simple values (1, 5);
insert into t_simple values (10, 5);
insert into t_simple values (4, 5);

上面的插入语句,二级索引列的值都是一样的,主键不是按顺序的,这样就可以看遍历时是不是按主键顺序存放的。


mysql> handler t_simple open as ts;
Query OK, 0 rows affected (0.00 sec)

mysql> handler ts read k_v next;
+----+------+
| id | v |
+----+------+
| 1 | 5 |
+----+------+
1 row in set (0.00 sec)

mysql> handler ts read k_v next;
+----+------+
| id | v |
+----+------+
| 4 | 5 |
+----+------+
1 row in set (0.00 sec)

mysql> handler ts read k_v next;
+----+------+
| id | v |
+----+------+
| 10 | 5 |
+----+------+
1 row in set (0.00 sec)

从结果可以看到,遍历的二级索引,值相等时,按主键的顺序遍历,基本可以确定二级索引不仅按索引列排序,还按主键值排序了。

为什么?

之前一直没看到说 MySQL 有这样的机制,问了前公司和先公司的 DBA 都没了解过这个。

最后 DBA 同事找到了 索引扩展, Index Extensions ,里面有这么段描述做了说明:

InnoDB automatically extends each secondary index by appending the primary key columns to it. Consider this table definition:


CREATE TABLE t1 (
i1 INT NOT NULL DEFAULT 0,
i2 INT NOT NULL DEFAULT 0,
d DATE DEFAULT NULL,
PRIMARY KEY (i1, i2),
INDEX k_d (d)
) ENGINE = InnoDB;

InnoDB 自动扩展每个二级索引,把主键值追加到索引列后面,把扩展后的组合列作为该索引的索引列。对于上面 t_simple 表的 k_v 索引,扩展后是 (v, id)列。

优化器会根据扩展后的二级索引的主键列来决定如何和是否使用那个索引。优化器可以用扩展的二级索引来进行 ref,range,index_merge 等类型的索引访问、松散的索引扫描、连接和排序优化,以及 min()/max() 优化。

可以用 show variables like '%optimizer_switch%'; 查看索引扩展是否开启;用 SET optimizer_switch = 'use_index_extensions=on/off'; 进行开启或关闭,这个只影响当前会话。

经测试,哪怕关闭了当前会话的索引扩展,用 handler 访问时仍然有按主键排序的效果。

来源:https://coderbee.net/index.php/db/20190106/1708

标签:mysql,innodb,二级索引
0
投稿

猜你喜欢

  • python break和continue用法对比

    2021-11-03 14:36:20
  • 轻松掌握SQL Server存储过程的命名标准

    2009-01-15 13:14:00
  • pandas统计重复值次数的方法实现

    2022-11-09 03:27:58
  • python将ip地址转换成整数的方法

    2022-10-17 23:40:23
  • 解决SpringBoot启动过后不能访问jsp页面的问题(超详细)

    2023-06-13 19:43:31
  • Python处理JSON时的值报错及编码报错的两则解决实录

    2023-11-10 07:12:07
  • python的sorted用法详解

    2022-04-09 20:21:14
  • tensorflow之如何使用GPU而不是CPU问题

    2023-07-06 13:05:48
  • 在python3.9下如何安装scrapy的方法

    2023-11-26 10:39:58
  • Python+Flask编写一个简单的行人检测API

    2023-09-26 17:55:19
  • Swift 3.0在集合类数据结构上的一些新变化总结

    2023-10-19 02:35:47
  • 用 Python 元类的特性实现 ORM 框架

    2022-02-12 12:45:24
  • GitHub上值得推荐的8个python 项目

    2021-01-11 22:40:12
  • python 字典(dict)遍历的四种方法性能测试报告

    2023-08-21 21:27:08
  • php !function_exists("T7FC56270E7A70FA81A5935B72EACBE29"))代码解密

    2023-11-21 14:36:02
  • 添加到各大流行网摘 书签的代码

    2008-04-20 14:15:00
  • Python打开文件、文件读写操作、with方式、文件常用函数实例分析

    2023-07-01 16:31:08
  • MySQL为数据表建立索引的原则详解

    2024-01-13 14:46:18
  • web.py在SAE中的Session问题解决方法(使用mysql存储)

    2024-01-28 02:17:15
  • 在opera里css出现渲染问题

    2009-01-15 12:19:00
  • asp之家 网络编程 m.aspxhome.com