MySQL索引大揭秘:优化查询的秘籍(下)

2023年 8月 9日 14.1k 0

本文为《MySQL归纳学习》专栏的第十篇文章,同时也是关于《MySQL索引》知识点的第二篇文章。

相关文章:

MySQL索引大揭秘:优化查询的秘籍(上)

在上一篇文章中,我们介绍了MySQL索引的基础知识,如今,迎来引领数据库查询速度的下篇!从覆盖索引、最左前缀原则,到索引下推,本文将用丰富案例演示,手把手教你优化查询性能,让你的应用轻松应对海量数据。而不容错过的是,还详细讲解了普通索引和唯一索引的区别,让你对索引的理解更上一层楼!

普通索引与唯一索引

我们基于如下表结构进行讲解:

CREATE TABLE t (
  id int(11) NOT NULL,
  k int(11) DEFAULT NULL,
  PRIMARY KEY (id)
) ENGINE=InnoDB;
insert into t(id, k) values(1,1),(2,2);

查询过程

普通索引跟唯一索引执行上的区别: 普通索引的等值查询,会继续遍历到第一个不相等的值才会结束,而唯一索引等值查询,命中则结束(性能差距微乎其微)。

InnoDB 按数据页读取数据,当需要读一条记录的时候,并不是将这个记录本身从磁盘读出来,而是以页为单位,将其整体读入内存。

那么普通索引的等值查询又分为哪些情况呢?为什么与唯一索引查询相比性能差距很小?

  • 一种要查询的记录不在页尾;
  • 一种是恰好在页尾(因索引的有序性,需要读取下一个数据页,此种情况复杂一些)

更新过程

在《Change Buffer内幕:从Merge到持久化的操作解析》一文中详细讲解了 change buffer ,我们再来看看它与普通索引、唯一索引之间的关联关系。

什么条件下可以使用 change buffer 呢?

对于唯一索引来说,所有的更新操作都要先判断这个操作是否违反唯一性约束。

要判断表中是否存在这个数据,而这必须要将数据页读入内存才能判断,如果都已经读入到内存了,那直接更新内存会更快,就没必要使用 change buffer 了。

那么普通索引和唯一索引在处理更新操作有什么区别呢?

如果要在表中插入一个新记录 (4,400) 的话,InnoDB 的处理流程是怎样的。

第一种情况是,这个记录要更新的目标页在内存中。这时,InnoDB 的处理流程如下:

  • 对于唯一索引来说,找到 3 和 5 之间的位置,判断到没有冲突,插入这个值,语句执行结束;
  • 对于普通索引来说,找到 3 和 5 之间的位置,插入这个值,语句执行结束。

这样看来,普通索引和唯一索引对更新语句性能影响的差别,只是一个判断,只会耗费微小的 CPU 时间。

第二种情况是,这个记录要更新的目标页不在内存中。这时,InnoDB 的处理流程如下:

  • 对于唯一索引来说,需要将数据页读入内存,判断到没有冲突,插入这个值,语句执行结束;
  • 对于普通索引来说,则是将更新记录在 change buffer,语句执行就结束了。

将数据从磁盘读入内存涉及随机 IO 的访问,是数据库里面成本最高的操作之一。change buffer 因为减少了随机磁盘访问,所以对更新性能的提升是会很明显的。

凡事并无绝对,普通索引和唯一索引的选择还是要结合具体的业务场景。比如说写多读少使用 changebuffer 可以加快执行速度(减少数据页磁盘 io);但是,如果业务模型是写后立马会做查询,则会触发 changebuff 立即 merge 到磁盘, 这样 的场景磁盘 io 次数不会减少,反而会增加 changebuffer 的维护代价。

**在写多读少的场景下,**如果将普通索引改为唯一索引,导致磁盘 IO 次数增多,进而导致某个业务的库内存命中率降低,整个系统处于阻塞状态,更新语句全部堵住。

覆盖索引

假设有如下表结构:

CREATE TABLE `t` (
`id` int(11) NOT NULL,
`city` varchar(16) NOT NULL,
`name` varchar(16) NOT NULL,
`age` int(11) NOT NULL,
`addr` varchar(128) DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `city` (`city`),
KEY `idx_name` (`name`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

--存储过程
CREATE DEFINER=`root`@`localhost` PROCEDURE `init_t`()
BEGIN
DECLARE i INT DEFAULT 0;
DECLARE j INT DEFAULT 0;
DECLARE k INT DEFAULT 0;
DECLARE city_list VARCHAR(200) DEFAULT '杭州,上海,武汉,北京';
DECLARE name_len INT DEFAULT 0;
DECLARE city VARCHAR(16);
DECLARE namef VARCHAR(16);
DECLARE age INT;
DECLARE addr VARCHAR(128);

SET name_len = LENGTH('abcdefghijklmnopqrstuvwxyz');

-- 第一层循环:按照city遍历
loop_city: LOOP
SET i = i + 1;
SET city = SUBSTRING_INDEX(SUBSTRING_INDEX(city_list, ',', i), ',', -1); -- 获取城市

-- 第二层循环:生成数据
while(k

相关文章

Oracle如何使用授予和撤销权限的语法和示例
Awesome Project: 探索 MatrixOrigin 云原生分布式数据库
下载丨66页PDF,云和恩墨技术通讯(2024年7月刊)
社区版oceanbase安装
Oracle 导出CSV工具-sqluldr2
ETL数据集成丨快速将MySQL数据迁移至Doris数据库

发布评论