MySQL对JOIN做了那些不为人知的优化《死磕MySQL系列 十七》

本文涉及的产品
RDS MySQL Serverless 基础系列,0.5-2RCU 50GB
云数据库 RDS MySQL,集群版 2核4GB 100GB
推荐场景:
搭建个人博客
RDS MySQL Serverless 高可用系列,价值2615元额度,1个月
简介: MySQL对JOIN做了那些不为人知的优化《死磕MySQL系列 十七》

大家好,我是咔咔 不期速成,日拱一卒


通过上期文章知道了在MySQL中存在三种join的算法,分别为NLJ、BNLJ、BNL,总结来说分为索引嵌套循环连接、缓存块嵌套循环连接、粗暴循环连接。


另外还知道了一个新的概念join_buffer,作用就是把关联表的数据全部读入join_buffer中,然后从join_buffer中一行一行的拿数据去被驱动表中查询。由于是在内存中获取数据,因此效率还是会有所提升。


同时在上期文章中遇到了一个陌生的概念hash_join,在上期中没有详细说明,本期会进行详述。




一、Multi-Range Read优化

在介绍本期主题时先来了解一个知识点Multi-Range Read,主要的作用是尽量让顺序读盘,在任何领域只要是有顺序的都会有一定的性能提升。


比如MySQL的索引,现在你应该知道索引天生具有有序性从而避免服务器对数据再次排序和建立临时表的问题。


接下来使用一个案例来实操一下这个优化是怎么做的


创建join_test1、join_test2两张表

CREATE TABLE `join_test1` (
 `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
 `a` int(11) unsigned NOT NULL,
 `b` int(11) unsigned NOT NULL,
 PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
CREATE TABLE `join_test2` (
 `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
 `a` int(11) unsigned NOT NULL,
 `b` int(11) unsigned NOT NULL,
 PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;

给两张表添加一些数据,用于案例演示


drop procedure idata;
delimiter ;;
create procedure idata()
begin
  declare i int;
  set i=1;
  while(i<=1000)do
    insert into join_test1 (a,b) values ( 1001-i, i);
    set i=i+1;
  end while;
  set i=1;
  while(i<=1000000)do
    insert into join_test2 (a,b)  values (i, i);
    set i=i+1;
  end while;
end;;
delimiter ;
call idata();

表join_test1的字段a上存在索引的,那么在查询时就会使用该索引。


执行流程大致为获取到字段a所有的值,然后根据a的值一行一行的进行回表到主键索引上获取数据


现在的情况是如果随着a的值递增顺序查询的话,id的值就会变相的为倒叙,虽然看起来是根据主键ID连续倒叙的,但在生产环境下肯定不是连续的,就会造成随机访问,那就肯定会造成性能变差。


为什么说随机访问会影响性能?


MySQL的索引天生具有有序性,同时MySQL也同样借鉴了局部性原理,局部性原理是数据和程序都默认有聚集成群的倾向,在访问到一行数据后,会有极大可能性再次访问到这条数据或这条数据相邻的数据。


现在你应该知道了MySQL在读取数据时并不是只读查询的数据,默认会读取16kb的数据,这个值是根据innodb_page_size决定的。


因此顺序查询是非常快的,是因为不用每次都通过执行器获取数据,而是直接在内存中获取,但若访问变为随机性就会每次通过执行器进行获取数据,所以这才是性能变差的原因。


MRR的作用


说了这么多现在你应该知道了MRR的作用就是把查询变为主键ID的递增查询,对磁盘的读尽可能的接近顺序读,就可以提升性能。


因此,执行语句的执行流程就会变成这样


先根据索a,获取到所有满足条件的数据,并且将主键id的值放入read_rnd_buffer中

在read_rnd_buffer中把id的值进行正序排序

再根据排序后得主键ID值,依次到主键索引上获取数据,并返回结果集

如何开启read_rnd_buffer


read_rnd_buffer的大小是由read_rnd_buffer_size参数控制的,默认值为256kb,但你要知道的是对于MRR的优化在优化器的判断策略中会更倾向于不使用,如果要使用则需要进行配置修改即可。


set optimizer_switch="mrr_cost_based=off"


mrr默认值


image.png


read_rnd_buffer存不下怎么办?


回忆下在上期中提到的join_buffer不够用是怎么处理的,会把上次读取的数据从buffer中清空,再放入剩下的数据,在MySQL中对于存储结果集的buffer内存不够情况下大多数都是这么处理的。


使用了read_rnd_buffer后的SQL执行流程就变成了这样


image.png


explain的结果显示


image.png


注意点


假设现在把查询范围扩大,看一下会有什么变化


image.png


可以看到当把范围扩大至接近全表数据时,会不再使用索引a从而进行了全表扫描,也就无法再使用mrr优化了


因此想要使用MRR进行提升性能是基于两个非常重要的点,一个是在索引上进行范围查询,另一个就是必须能使用上索引,当然这个索引要是范围查询的列


相关实践学习
如何在云端创建MySQL数据库
开始实验后,系统会自动创建一台自建MySQL的 源数据库 ECS 实例和一台 目标数据库 RDS。
全面了解阿里云能为你做什么
阿里云在全球各地部署高效节能的绿色数据中心,利用清洁计算为万物互联的新世界提供源源不断的能源动力,目前开服的区域包括中国(华北、华东、华南、香港)、新加坡、美国(美东、美西)、欧洲、中东、澳大利亚、日本。目前阿里云的产品涵盖弹性计算、数据库、存储与CDN、分析与搜索、云通信、网络、管理与监控、应用服务、互联网中间件、移动服务、视频服务等。通过本课程,来了解阿里云能够为你的业务带来哪些帮助 &nbsp; &nbsp; 相关的阿里云产品:云服务器ECS 云服务器 ECS(Elastic Compute Service)是一种弹性可伸缩的计算服务,助您降低 IT 成本,提升运维效率,使您更专注于核心业务创新。产品详情: https://www.aliyun.com/product/ecs
相关文章
|
9天前
|
存储 关系型数据库 MySQL
mysql optimizer_switch : 查询优化器优化策略深入解析
mysql optimizer_switch : 查询优化器优化策略深入解析
|
9天前
|
关系型数据库 MySQL 数据库
MySQL索引优化:深入理解索引合并
MySQL索引优化:深入理解索引合并
|
2天前
|
关系型数据库 MySQL 数据库
MySQL索引的类型与优化方法
MySQL索引的类型与优化方法
|
3天前
|
SQL Oracle 关系型数据库
MySQL单表千万级数据查询优化大家怎么说(评论有亮点)
单表千万级数据是MySQL查询的一个坎,可能还不是天花板。“一个人走的慢,一群人走的快”,通过讨论可以发现MySQL千万数据的全貌大概是怎样的。
15 0
|
4天前
|
存储 关系型数据库 MySQL
MySQL索引设计原则与优化策略
MySQL索引设计原则与优化策略
|
4天前
|
关系型数据库 MySQL 数据库
MySQL索引的类型与优化方法
MySQL索引的类型与优化方法
|
5天前
|
存储 关系型数据库 MySQL
MySQL索引设计原则与优化策略
MySQL索引设计原则与优化策略
|
5天前
|
关系型数据库 MySQL 索引
MySQL之优化SELECT语句
以上只是一些基本的优化策略,具体的优化方案还需要根据实际的业务需求和数据情况来定制。
8 0
|
9天前
|
算法 关系型数据库 MySQL
深入理解MySQL中的JOIN算法
深入理解MySQL中的JOIN算法
|
10天前
|
存储 关系型数据库 MySQL
技术笔记:MySQL数据库优化详解(收藏)
技术笔记:MySQL数据库优化详解(收藏)
16 0