mysql mrr_cost_based_技术分享 | 用好 MySQL 的 MRR 优化器
作者:蒋乐兴
MySQL DBA,擅长 python 和 SQL,目前维护着 github 的两个开源项目:mysqltools 、dbmc 以及独立博客:https://www.sqlpy.com。
本文来源:原创投稿
*爱可生开源社区出品,原创内容未经授权不得随意使用,转载请联系小编并注明来源。
MRR 要解决的问题
MRR 是 MySQL 针对特定查询的一种优化手段。假设一个查询有二级索引可用,读完二级索引后要回表才能查到那些不在当前二级索引上的列值,由于二级索引上引用的主键值不一定是有序的,因此就有可能造成大量的随机 IO,如果回表前把主键值给它排一下序,那么在回表的时候就可以用顺序 IO 取代原本的随机 IO。
环境准备
为了实验我们要准备一下表结构和数据。
-- 创建表
mysql> show create table t;
+----------------------------------------------------------------------+
| Table | Create Table |
+----------------------------------------------------------------------+
| t | CREATE TABLE `t` (
`id` int NOT NULL AUTO_INCREMENT,
`i0` int NOT NULL,
`i1` int NOT NULL,
`i2` int NOT NULL,
`i3` int NOT NULL,
`c0` varchar(128) NOT NULL,
`c1` varchar(128) NOT NULL,
`f0` float NOT NULL,
`f1` float NOT NULL,
PRIMARY KEY (`id`),
KEY `idx_i0` (`i0`)
) ENGINE=InnoDB
+----------------------------------------------------------------------+
1 row in set (0.00 sec)
-- 造数据
mysql> select count(*) from t;
+----------+
| count(*) |
+----------+
| 1120000 |
+----------+
1 row in set (0.77 sec)
--
update t set i0 = id % 100;
MRR 的优化效果
1. 有 MRR 优化(Using MRR)时 SQL 的耗时情况。
mysql> explain select i0,i3 from t where i0 between 1 and 2;
+----+-------------+-------+------------+-------+---------------+--------+---------+------+-------+----------+----------------------------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+-------+---------------+--------+---------+------+-------+----------+----------------------------------+
| 1 | SIMPLE | t | NULL | range | idx_i0 | idx_i0 | 4 | NULL | 43968 | 100.00 | Using index condition; Using MRR |
+----+-------------+-------+------------+-------+---------------+--------+---------+------+-------+----------+----------------------------------+
1 row in set, 1 warning (0.00 sec)
mysql> select i0,i3 from t where i0 between 1 and 2;
22400 rows in set (0.80 sec)
2. 关闭 MRR 优化。
set optimizer_switch = 'index_merge=on,index_merge_union=on,index_merge_sort_union=on,index_merge_intersection=on,engine_condition_pushdown=on,index_condition_pushdown=on,mrr=off,mrr_cost_based=on,block_nested_loop=on,batched_key_access=off,materialization=on,semijoin=on,loosescan=on,firstmatch=on,duplicateweedout=on,subquery_materialization_cost_based=on,use_index_extensions=on,condition_fanout_filter=on,derived_merge=on,use_invisible_indexes=off,skip_scan=on,hash_join=on';
mysql> explain select i0,i3 from t where i0 between 1 and 2;
+----+-------------+-------+------------+-------+---------------+--------+---------+------+-------+----------+-----------------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+-------+---------------+--------+---------+------+-------+----------+-----------------------+
| 1 | SIMPLE | t | NULL | range | idx_i0 | idx_i0 | 4 | NULL | 43968 | 100.00 | Using index condition |
+----+-------------+-------+------------+-------+---------------+--------+---------+------+-------+----------+-----------------------+
1 row in set, 1 warning (0.00 sec)
mysql> select i0,i3 from t where i0 between 1 and 2;
22400 rows in set (2.56 sec)结论
就刚才的测试场景开启 MRR 优化可以得到 3 倍的性能提升。
MRR 的优化器参数调整
如果想关闭 MRR 优化的话,就要把优化器开关 mrr 设置为 off。
默认只有在优化器认为 MRR 可以带来优化的情况下才会走 MRR,如果你想不管什么时候能走 MRR 的都走 MRR 的话,你要把 mrr_cost_based 设置为 off,不过最好不要这么干,因为这确实是一个坑,MRR 不一定什么时候都好,全表扫描有时候会更加快,如果在这种场景下走 MRR 就完成了。
开启 MRR 关闭基于开销的优化。
-- mrr=on,mrr_cost_based=off
set optimizer_switch = 'index_merge=on,index_merge_union=on,index_merge_sort_union=on,index_merge_intersection=on,engine_condition_pushdown=on,index_condition_pushdown=on,mrr=on,mrr_cost_based=off,block_nested_loop=on,batched_key_access=off,materialization=on,semijoin=on,loosescan=on,firstmatch=on,duplicateweedout=on,subquery_materialization_cost_based=on,use_index_extensions=on,condition_fanout_filter=on,derived_merge=on,use_invisible_indexes=off,skip_scan=on,hash_join=on';
mysql> explain select i0,i3 from t where i0 between 1 and 10;
+----+-------------+-------+------------+-------+---------------+--------+---------+------+--------+----------+----------------------------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+-------+---------------+--------+---------+------+--------+----------+----------------------------------+
| 1 | SIMPLE | t | NULL | range | idx_i0 | idx_i0 | 4 | NULL | 218492 | 100.00 | Using index condition; Using MRR |
+----+-------------+-------+------------+-------+---------------+--------+---------+------+--------+----------+----------------------------------+
1 row in set, 1 warning (0.00 sec)
select i0,i3 from t where i0 between 1 and 10;
112000 rows in set (4.86 sec)
开启 MRR 开启基于开销的优化。
-- mrr=on,mrr_cost_based=on
set optimizer_switch = 'index_merge=on,index_merge_union=on,index_merge_sort_union=on,index_merge_intersection=on,engine_condition_pushdown=on,index_condition_pushdown=on,mrr=on,mrr_cost_based=on,block_nested_loop=on,batched_key_access=off,materialization=on,semijoin=on,loosescan=on,firstmatch=on,duplicateweedout=on,subquery_materialization_cost_based=on,use_index_extensions=on,condition_fanout_filter=on,derived_merge=on,use_invisible_indexes=off,skip_scan=on,hash_join=on';
mysql> explain select i0,i3 from t where i0 between 1 and 10;
+----+-------------+-------+------------+------+---------------+------+---------+------+---------+----------+-------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+------+---------------+------+---------+------+---------+----------+-------------+
| 1 | SIMPLE | t | NULL | ALL | idx_i0 | NULL | NULL | NULL | 1121902 | 19.48 | Using where |
+----+-------------+-------+------------+------+---------------+------+---------+------+---------+----------+-------------+
1 row in set, 1 warning (0.00 sec)
mysql> select i0,i3 from t where i0 between 1 and 10;
112000 rows in set (1.52 sec)
可以看到当 mrr_cost_based = OFF 的情况下用时 4.86s,mrr_cost_based = ON 的情况下用时 1.52s,总的来说 mrr_cost_based 是非常关键的建议始终打开。
MRR 的参数优化
MRR 要把主键排个序,这样之后对磁盘的操作就是由顺序读代替之前的随机读。从资源的使用情况上来看就是让 CPU 和内存多做点事,来换磁盘的顺序读。然而排序是需要内存的,这块内存的大小就由参数 read_rnd_buffer_size 来控制。
read_rnd_buffer_size 太小无法启用 MRR 功能。
mysql> select @@read_rnd_buffer_size;
+------------------------+
| @@read_rnd_buffer_size |
+------------------------+
| 262144 |
+------------------------+
1 row in set (0.00 sec)
mysql> explain select i0,i3 from t where i0 between 1 and 12;
+----+-------------+-------+------------+------+---------------+------+---------+------+---------+----------+-------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+------+---------------+------+---------+------+---------+----------+-------------+
| 1 | SIMPLE | t | NULL | ALL | idx_i0 | NULL | NULL | NULL | 1121902 | 23.57 | Using where |
+----+-------------+-------+------------+------+---------------+------+---------+------+---------+----------+-------------+
1 row in set, 1 warning (0.00 sec)
放大 read_rnd_buffer_size 让 MySQL 有足够的资源用于 MRR 。
mysql> set read_rnd_buffer_size = 32 * 1024 * 1024;
Query OK, 0 rows affected (0.00 sec)
mysql> explain select i0,i3 from t where i0 between 1 and 12;
+----+-------------+-------+------------+-------+---------------+--------+---------+------+--------+----------+----------------------------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+-------+---------------+--------+---------+------+--------+----------+----------------------------------+
| 1 | SIMPLE | t | NULL | range | idx_i0 | idx_i0 | 4 | NULL | 264436 | 100.00 | Using index condition; Using MRR |
+----+-------------+-------+------------+-------+---------------+--------+---------+------+--------+----------+----------------------------------+
1 row in set, 1 warning (0.00 sec)
mysql mrr_cost_based_技术分享 | 用好 MySQL 的 MRR 优化器相关推荐
- mysql数据库j电子课件,MYSQL数据库技术分享PPT演示课件
<MYSQL数据库技术分享PPT演示课件>由会员分享,可在线阅读,更多相关<MYSQL数据库技术分享PPT演示课件(29页珍藏版)>请在人人文库网上搜索. 1.数据库技术分享, ...
- mysql mts_技术分享 | 回顾 MySQL 的 MTS
作者:洪斌 爱可生南区负责人兼技术服务总监,MySQL ACE,擅长数据库架构规划.故障诊断.性能优化分析,实践经验丰富,帮助各行业客户解决 MySQL 技术问题,为金融.运营商.互联网等行业客户提供 ...
- mysql+odbc+ado_MFC ado+mysql+odbc技术分享
第一步:建立数据库 假设有一个sql文件 mysql>use dbname; //创建一个数据库名为dbname的数据库(空数据库) mysql>set ...
- mysql dbuild_config_技术分享 | MySQL Test 初探
什么是 MySQL Test? MySQL Test 是 MySQL 发行版本中集成 all-in-one 测试框架,用于做 mysql 服务的单元,回归和一致性测试,并提供了运行单元测试和创建新单元 ...
- mysql tcmalloc_技术分享 | tcmalloc解决mysqld实例引发的cpu过高问题
原创:任坤作者简介 任坤,现居珠海,先后担任专职 Oracle 和 MySQL DBA,现在主要负责 MySQL.mongoDB 和 Redis 维护工作. 背景 MySQL 版本:5.6.29,普通 ...
- 技术分享 | 浅谈 MySQL 的临时表和临时文件
作者:姚嵩 爱可生南区交付服务部经理,爱好音乐,动漫,电影,游戏,人文,美食,旅游,还有其他.虽然都很菜,但毕竟是爱好. 本文来源:原创投稿 *爱可生开源社区出品,原创内容未经授权不得随意使用,转载请 ...
- mysql relay_技术分享 | MySQL:产生大量小 relay log 的故障一例
作者:高鹏 文章末尾有他著作的<深入理解 MySQL 主从原理 32 讲>,深入透彻理解 MySQL 主从,GTID 相关技术知识. 源码版本:5.7.22 水平有限,如有误请谅解 本文建 ...
- MySQL mdl导入_技术分享 | 深入理解 MySQL MDL Lock
作者:高鹏(网名八怪) 文章末尾有他著作的<深入理解 MySQL 主从原理 32 讲>,深入透彻理解 MySQL 主从,GTID 相关技术知识. 本文来源:转载自公众号-mysql cod ...
- mysql 1280_技术分享 | MySQL 一次奇怪的故障分析
一.问题来源 这是一个朋友问我的典型案例.整个故障现象表现为,MySQL 数据库频繁的出现大量的请求不能响应.下面是一些他提供的证据: 1. show processlist从状态信息来看出现如下情况 ...
最新文章
- 面试题:如何理解 Linux 的零拷贝技术?
- Twitter 广告平台实时计费系统的架构增强之道
- VTK:给定颜色,找到对比色用法实战
- javascript学习系列(15):数组中的lastIndexOf方法
- Idea Marketplace 加载很慢 加载不出来
- 数据千万条,备份第一条:VFEmail被擦除所有数据面临关停
- Oracle的where子句
- 【BZOJ1085】迭代加深+启发式搜索
- 理解Java - JDK动态代理原理
- Android 表单之 EditText(输入框)详解
- Windows之IOCP
- 分页存储管理系统中内存有效访问时间的计算
- 正点原子Linux开发板实现WiFi开机自动连接
- Markdown:插入图片和图注
- Latex文档中 插入符号和编号
- 【财务_会计3_1】会计科目的概念
- GitQ 导读: 为什么 GitHub ?
- 传奇服务器系统版本,[资料]服务端详细解释
- java web 组态,Java:Eclipse中使用WTP开发Web项目
- 计算机分子模拟的意义包括,计算机分子模拟