在讲ICP和MRR之前,我们先了解一下MySQL的架构。于本文的重点并不在架构细节上,所以让我们直接看关键部分,存储引擎作为单独的一层,是连接底层存储系统和上层server其他部分的桥梁,而MySQL对存储引擎的抽象也极大地丰富了其可扩展性。

ICP

我们平时需要查询和写入的数据最终的存储介质都是底层的文件系统,而数据的传输必然要经过上面说到的两个部分。就好像我们将单体服务切分成微服务一样,如果我们需要从另一个服务取数据,需要由对方服务查询DB,然后通过网络再传输给自己。查询DB的过程也是一种网络传输,所以这里的数据其实经过了两次网络传输,相比直接查询查询DB增加了一倍的网络消耗。这是微服务切分后带来的代价,那有没有其他的途径可以减少这种消耗呢?

如果我实际需要10条数据,但每次都获取100条数据后自己执行过滤,那两次网络传输的都是100条;如果由对方服务进行过滤,则其中一次网络只需传输10条;更甚者,如果对方查DB直接只查10条,那两次网络传输都是10条。

ICP(Index Condition Pushdown)就是利用了类似的原理来达到性能优化的。举个?

有这么一张表employees,其中first_name和last_name建立了一个联合索引。

CREATE TABLE `employees` (  `emp_no` int(11) NOT NULL,  `birth_date` date NOT NULL,  `first_name` varchar(14) NOT NULL,  `last_name` varchar(16) NOT NULL,  `gender` enum('M','F') NOT NULL,  `hire_date` date NOT NULL,  PRIMARY KEY (`emp_no`),  KEY `birth` (`birth_date`),  KEY `first_last` (`first_name`,`last_name`)) ENGINE=InnoDB DEFAULT CHARSET=utf8;

执行以下语句,MySQL会怎么处理呢?

SELECT * FROM employees WHERE first_name = "Georgi" AND last_name LIKE "Maliniak%"

在5.6版本以前,MySQL会使用first_last这个联合索引,然后存储引擎根据first_name = "Georgi"这个过滤条件查询相应的记录,返回上层的server后再根据last_name LIKE "Maliniak%"这个条件做过滤,最后将符合条件的数据返回给用户。也就是说,last_name LIKE "Maliniak%"这个条件是经历了两次数据传输后才起的作用,查询的扫描行数就是first_name = "Georgi"的所有记录,跟我们一开始举的例子很像,是存在优化空间的。

5.6版本及以后呢,MySQL不这么干了。走索引的时候虽然拿不到完整的数据文件,但是last_name就是索引中的字段,这个信息其实在first_last这个联合索引文件中是存在的,所以提前过滤完全可以做到。MySQL将where条件下推到存储引擎,存储引擎尽可能的根据条件中的信息查询记录,最终存储引擎获取到的就是符合first_name = "Georgi" AND last_name LIKE "Maliniak%"的记录,这样在最底层就避免了额外的数据传输。

ICP是可以通过开关控制的,而且它有特别的适用场景,并不是无脑能用的:

  1. ICP适用于连接类型为range,ref,eq_ref和ref_or_null的全表扫描,连接类型就explain输出中的type字段;

  2. InnoDB和MyISAM都支持ICP;

  3. ICP适用于二级索引,不适用于聚簇索引(一般就是主键),因为聚簇索引的叶子节点就是数据记录,在存在缓冲区的情况下,ICP起不到啥作用;

  4. 二级索引如果含有虚拟列的话不支持ICP;

  5. 条件中如果用到了子查询,那不会被下推;

默认情况下ICP是打开的,如果你愿意(除了测试应该没人会这么做的吧),可以通过SELECT @@optimizer_switch查看,通过SET optimizer_switch = 'index_condition_pushdown=off'关闭这个功能。

MRR

MRR(Multi-Range Read)则是从硬件层面切入,但目标和ICP是一致的,就是优化查询效率。我们都知道对于机械硬盘来说,由于其物理结构的限制,随机读的效率远低于顺序读。大致结构长下面这样,凑合看吧。

因为转速有上限,所以单位时间内磁头的移动距离也有限制,最坏的情况下,7200 RPM的机械硬盘每秒寻道120次,1次寻道读取一条记录,那1秒内只能返回120条记录,完全是凉凉的节奏。而如果单次寻道能够匹配多条记录,那效率就能成倍上升,这就是顺序读的优势,更何况还有page cache预读等黑科技加持,顺序读和随机读简直就像跑车和自行车。

说了这么多好像跟MRR没太大关系,我们想象一种场景:通过二级索引查询数据库记录,此时不管是MyISAM还是InnoDB的存储引擎(假设无法用到覆盖索引),都需要一个回表操作,而二级索引的顺序和数据记录的顺序可能完全不一致,这次查询的流程就像下面这张图一样(来源于MariaDB官网,MariaDB是MySQL的一个分支,好多功能类似)

图中的红线是查询过程,而蓝色则是磁盘的运动轨迹,可以看到上上下下,很明显磁盘在做随机读。MRR就是一种将这样的随机读转变为顺序读的骚操作。我们看一下加入了MRR后整个流程会有哪些变化:

中间多了一步排序,拿InnoDB举例,聚簇索引(一般就是主键索引,除非你没设置主键)的叶子节点存放的就是数据记录本身,也就是说,聚簇索引和数据在磁盘的存储顺序其实是一致的。当我们通过二级索引拿到聚簇索引的值后,先对其进行排序,然后再去磁盘查询,这样原来的随机读就被转化为了顺序读。除了将随机读转换为顺序读以外,MRR还有索引批量访问的优点,比如对索引进行范围访问或者将索引列作为连接属性进行连接查询,MRR能够累积一定数量的查询key然后进行批量查询。

当我们用EXPLAIN输出某条SQL的执行计划的时候,如果MySQL判断会用到MRR,则会在Extra这一列说明Using MRR。

虽然MRR看上去是一个很牛逼的优化,但也并不能做到一招鲜吃遍天。最简单的,如果我只查询一条数据,哪需要什么排序呢?而中间的排序过程其实需要将第一步的结果放入一个临时的缓冲区,可以将MRR看成一种空间换时间的方法。既然MRR有时候没有优势,所以机智的MySQL增加了好几个配置来处理这些情况:

  1. mrr——如果值为off,mrr永远不会被使用,on则有可能会被使用;

  2. mrr_cost_based——智能决策,如果发现mrr划算则使用,否则不使用;

  3. read_rnd_buffer_size——控制排序步骤缓冲区的大小,如果满了就会先执行一次查询。

这三个属性值前两个可以通过SELECT @@optimizer_switch查看,第3个则可以通过SHOW VARIABLES查看。我的测试版本是5.7.29,这三个属性的默认值分别是on,on,262144(也就是256KB)。

最后说一句(求关注)

本人才疏学浅,难免会有纰漏,如果你发现了错误的地方,可以在留言区提出来,我对其加以修改。

感谢您的阅读,十分欢迎并感谢您的关注。我是未得,一条梦想有一天能够翻身的咸鱼,如果你也喜欢唠嗑但又不仅仅是唠嗑,最好是唠嗑的时候还能学点技术,那就来关注我吧。

mysql 一对多 根据多条数据排序_优化的道路永无止境——Mysql的ICP及MRR相关推荐

  1. mysql 同一张表 某个字段更新到另一条数据上_面试基础:数据库MySQL基础入门(下)...

    本文是面试基础的第二篇.本篇偏理论,包括三节: 事务和并发 数据库设计 索引 所选的三个内容均是面试的高频考察点,需要细致地理解 No.1     事务和并发 事务:数据库操作的基本单元.对于数据库的 ...

  2. mysql using mrr_优化的道路永无止境——Mysql的ICP及MRR

    在讲ICP和MRR之前,我们先了解一下MySQL的架构.于本文的重点并不在架构细节上,所以让我们直接看关键部分,存储引擎作为单独的一层,是连接底层存储系统和上层server其他部分的桥梁,而MySQL ...

  3. mysql单表瓶颈_mysql单表性能瓶颈_优化系列 | 实例解析MySQL性能瓶颈排查定位-云栖社区-阿里云...

    导读 从一个现场说起,全程解析如何定位性能瓶颈. 排查过程 收到线上某业务后端的MySQL实例负载比较高的告警信息,于是登入服务器检查确认. 1. 首先我们进行OS层面的检查确认 登入服务器后,我们的 ...

  4. php mysql只获取一条数据_php mysql 查询只返回第一条数据

    php mysql 查询只返回第一条数据 $search = mysql_query("select * from `info`"); $search = mysql_fetch_ ...

  5. mysql快速导入5000万条数据过程记录(LOAD DATA INFILE方式)

    mysql快速导入5000万条数据过程记录(LOAD DATA INFILE方式) 首先将要导入的数据文件top5000W.txt放入到数据库数据目录/var/local/mysql/data/${d ...

  6. mysql通过命令添加1条数据

    mysql通过命令添加1条数据 1.语法: INSERT INTO 表名 (字段1,字段2,...字段n) VALUES (值1,值2,...值n); 2.语法解析: INSERT INTO ... ...

  7. php 分表 实战,PHP实战:1亿条数据如何分表100张到Mysql数据库中(PHP)

    <PHP实战:1亿条数据如何分表100张到Mysql数据库中(PHP)>要点: 本文介绍了PHP实战:1亿条数据如何分表100张到Mysql数据库中(PHP),希望对您有用.如果有疑问,可 ...

  8. MySQL 快速批量创建千万条数据 千万级数据

     MySQL 快速批量创建千万条数据 千万级数据 一.实现思路 1.创建一张users表,数据库引擎调整为: MyISAM 2.使用存储过程实现,用循环语句,批量插入1kw次.( WHILE ... ...

  9. mysql 一对多 关联一条最新的数据_不得不会的mysql锁

    6. 多表之间的关系 如图,实际业务数据库中的表之间都是有关系的,我们接下来主要要学习的就是如何分析表关系及建立表关系. 分类表 create table category( cid varchar( ...

最新文章

  1. 10.Redis 性能测试
  2. yandex注册验证码怎么填_注册资本到底怎么填?
  3. HeartBeat Install
  4. CentOS7 安装并使用Ovirt 4.2
  5. filezilla 设置服务器_java项目部署到linux服务器,微信小程序后台springboot项目部署到腾讯云服务器(图文详解)...
  6. [TCP/IP] 关闭连接后为什么客户端最后还要等待2MSL
  7. 解决flash的虚框问题
  8. MongoDB集群安装与配置2.4.3版本
  9. oracle共享内存段手工清理
  10. 易语言卷帘菜单与json_易语言卷帘式菜单加背景图片源码
  11. win7美化_Windows 美化资源大合集!
  12. 使用Magoshare for Mac无法打开恢复的文件或扫描后找不到丢失的文件怎么办?
  13. 人工智能与就业系列调研 | 老板电器的新蝶变
  14. 什么是 Google Play服务
  15. 递归,举几个简单的例子
  16. RuntimeError: mat1 and mat2 shapes cannot be multiplied (5760x6 and 128x4)
  17. 一键将苹果实况照片livp转jpg的简单方法
  18. 数据结构算法-二叉树
  19. TestOpenWriter
  20. 【项目管理】--- 时间管理 --- 缩短工期

热门文章

  1. 最小二乘法求线性回归方程_规范解题第14期 利用“最小二乘法”原理解一道题...
  2. python和循环语句_Python 小白零基础入门 -- 条件语句和循环语句
  3. mie散射理论方程_散射,原子分子散射
  4. java发牌程序_求解,用JAVA写洗牌与发牌模拟程序
  5. java cookie过期_在Express中,会话Cookie过期
  6. python对数组分类_有效地从字典Python中对数组进行分类
  7. chrome vue 未响应_vue之骨架屏踩坑之路
  8. 用python语言实现反恐精英基础版-案例
  9. 面向对象思想的设计原则概述
  10. 微服务分布式企业框架 Springmvc+mybatis+shiro+Dubbo+ZooKeeper