优化的道路永无止境——Mysql的ICP及MRR

在讲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会怎么处理呢?

EXPLAIN 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)。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 217,734评论 6 505
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,931评论 3 394
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,133评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,532评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,585评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,462评论 1 302
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,262评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,153评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,587评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,792评论 3 336
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,919评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,635评论 5 345
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,237评论 3 329
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,855评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,983评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,048评论 3 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,864评论 2 354

推荐阅读更多精彩内容