MySQL为什么错误选择代价更大的索引
MySQL优化器索引选择迷思。
高鹏(八怪)对本文亦有贡献。
1. 问题描述
群友提出问题,表里有两个列c1、c2,分别为INT、VARCHAR类型,且分别创建了unique key。
SQL查询的条件是 WHERE c1 = ? AND c2 = ?
,用EXPLAIN查看执行计划,发现优化器优先选择了VARCHAR类型的c2列索引。
他表示很不理解,难道不应该选择看起来代价更小的INT类型的c1列吗?
2. 问题复现
创建测试表t1:
[root@yejr.run]> CREATE TABLE `t1` (`c1` int NOT NULL AUTO_INCREMENT,`c2` int unsigned NOT NULL,`c3` varchar(20) NOT NULL,`c4` varchar(20) NOT NULL,PRIMARY KEY (`c1`),UNIQUE KEY `k3` (`c3`),UNIQUE KEY `k2` (`c2`)
) ENGINE=InnoDB;
利用 mysql_random_data_load
写入一万行数据:
mysql_random_data_load -h127.0.0.1 -uX -pX yejr t1 10000
查看执行计划:
[root@yejr.run]> EXPLAIN SELECT * FROM t1 WHEREc2 = 1755950419 AND c3 = 'MichaelaAnderson'\G
*************************** 1. row ***************************id: 1select_type: SIMPLEtable: t1partitions: NULLtype: const
possible_keys: k3,k2key: k3key_len: 82ref: constrows: 1filtered: 100.00Extra: NULL
可以看到优化器的确选择了 k3 索引,而非"预期"的 k2 索引,这是为什么呢?
3. 问题分析
其实原因很简单粗暴:优化器认为这两个索引选择的代价都是一样的,只是优先选中排在前面的那个索引而已。
再建一个相同的表 t2,只不过把 k2、k3 的索引创建顺序对调下:
[root@yejr.run]> CREATE TABLE `t2` (`c1` int NOT NULL AUTO_INCREMENT,`c2` int unsigned NOT NULL,`c3` varchar(20) NOT NULL,`c4` varchar(20) NOT NULL,PRIMARY KEY (`c1`),UNIQUE KEY `k2` (`c2`),UNIQUE KEY `k3` (`c3`)
) ENGINE=InnoDB;
再查看执行计划:
[root@yejr.run]> EXPLAIN SELECT * FROM t2 WHEREc2 = 1755950419 AND c3 = 'MichaelaAnderson'\G
*************************** 1. row ***************************id: 1select_type: SIMPLEtable: t1partitions: NULLtype: const
possible_keys: k2,k3key: k2key_len: 4ref: constrows: 1filtered: 100.00Extra: NULL
我们利用 EXPLAIN ANALYZE
来查看下两次执行计划的代价对比:
-- 查看t1表执行计划代价
[root@yejr.run]> EXPLAIN ANALYZE SELECT * FROM t1 WHEREc2 = 1755950419 AND c3 = 'MichaelaAnderson'\G
*************************** 1. row ***************************
EXPLAIN: -> Rows fetched before execution (cost=0.00..0.00 rows=1) (actual time=0.000..0.000 rows=1 loops=1)-- 查看t2表执行计划代价
[root@yejr.run]> EXPLAIN ANALYZE SELECT * FROM t2 WHERE c2 = 1755950419 AND c3 = 'MichaelaAnderson'\G
*************************** 1. row ***************************
EXPLAIN: -> Rows fetched before execution (cost=0.00..0.00 rows=1) (actual time=0.000..0.000 rows=1 loops=1)
可以看到,很明显代价都是一样的。
再利用 OPTIMIZE_TRACE
查看执行计划,也能看到两个SQL的代价是一样的:
...{"rows_estimation": [{"table": "`t1`","rows": 1,"cost": 1,"table_type": "const","empty": false}]},
...
所以,优化器认为选择哪个索引都是一样的,就看哪个索引排序更靠前。
从执行SELECT时的debug trace结果也能佐证:
-- 1、 T1表,k3索引在前面PRIMARY KEY (`c1`),UNIQUE KEY `k3` (`c3`),UNIQUE KEY `k2` (`c2`)T@2: | | | | | | | | opt: (null): starting struct
T@2: | | | | | | | | opt: table: "`t1`"
T@2: | | | | | | | | opt: field: "c3" (C3在前面,因此最后使用k3)
T@2: | | | | | | | | >convert_string
T@2: | | | | | | | | | >alloc_root
T@2: | | | | | | | | | | enter: root: 0x40a8068
T@2: | | | | | | | | | | exit: ptr: 0x4b41ab0
T@2: | | | | | | | | | <alloc_root 304
T@2: | | | | | | | | <convert_string 2610
T@2: | | | | | | | | opt: equals: "'Louise Garrett'"
T@2: | | | | | | | | opt: null_rejecting: 0
T@2: | | | | | | | | opt: (null): ending struct
T@2: | | | | | | | | opt: Key_use: optimize= 0 used_tables=0x0 ref_table_rows= 18446744073709551615 keypart_map= 1
T@2: | | | | | | | | opt: (null): starting struct
T@2: | | | | | | | | opt: table: "`t1`"
T@2: | | | | | | | | opt: field: "c2"
T@2: | | | | | | | | opt: equals: "22896242"
T@2: | | | | | | | | opt: null_rejecting: 0
T@2: | | | | | | | | opt: null_rejecting: 0
T@2: | | | | | | | | opt: (null): ending struct
T@2: | | | | | | | | opt: Key_use: optimize= 0 used_tables=0x0 ref_table_rows= 18446744073709551615 keypart_map= 1
T@2: | | | | | | | | opt: (null): starting struct
T@2: | | | | | | | | opt: table: "`t1`"
T@2: | | | | | | | | opt: field: "c2"
T@2: | | | | | | | | opt: equals: "22896242"
T@2: | | | | | | | | opt: null_rejecting: 0
T@2: | | | | | | | | opt: (null): ending struct
T@2: | | | | | | | | opt: ref_optimizer_key_uses: ending struct
T@2: | | | | | | | | opt: (null): ending struct-- 2、 T2表,k2索引在前面PRIMARY KEY (`c1`),UNIQUE KEY `k2` (`c2`),UNIQUE KEY `k3` (`c3`)T@2: | | | | | | | | opt: (null): starting struct
T@2: | | | | | | | | opt: table: "`t2`"
T@2: | | | | | | | | opt: field: "c2" (C2在前面因此使用k2索引)
T@2: | | | | | | | | opt: equals: "22896242"
T@2: | | | | | | | | opt: null_rejecting: 0
T@2: | | | | | | | | opt: (null): ending struct
T@2: | | | | | | | | opt: Key_use: optimize= 0 used_tables=0x0 ref_table_rows= 18446744073709551615 keypart_map= 1
T@2: | | | | | | | | opt: (null): starting struct
T@2: | | | | | | | | opt: table: "`t2`"
T@2: | | | | | | | | opt: field: "c3"
T@2: | | | | | | | | >convert_string
T@2: | | | | | | | | | >alloc_root
T@2: | | | | | | | | | | enter: root: 0x40a8068
T@2: | | | | | | | | | | exit: ptr: 0x4b41ab0
T@2: | | | | | | | | | <alloc_root 304
T@2: | | | | | | | | <convert_string 2610
T@2: | | | | | | | | opt: equals: "'Louise Garrett'"
T@2: | | | | | | | | opt: null_rejecting: 0
T@2: | | | | | | | | opt: (null): ending struct
T@2: | | | | | | | | opt: ref_optimizer_key_uses: ending struct
T@2: | | | | | | | | opt: (null): ending struct
4. 问题延伸
到这里,我们不禁有疑问,这两个索引的代价真的是一样吗?
就让我们用 mysqlslap
来做个简单对比测试吧:
-- 测试1:对c2列随机point select
mysqlslap -hlocalhost -uroot -Smysql.sock --no-drop --create-schema X -i 3 --number-of-queries 1000000 -q "set @xid = cast(round(rand()*2147265929) as unsigned); select * from t1 where c2 = @xid" -c 8
...Average number of seconds to run all queries: 9.483 seconds
...-- 测试2:对c3列随机point select
mysqlslap -hlocalhost -uroot -Smysql.sock --no-drop --create-schema X -i 3 --number-of-queries 1000000 -q "set @xid = concat('u',cast(round(rand()*2147265929) as unsigned)); select * from t1 where c3 = @xid" -c 8
...Average number of seconds to run all queries: 10.360 seconds
...
可以看到,如果是走 c3 列索引,耗时会比走 c2 列索引多出来约 7% ~ 9%(在我的环境下测试的结果,不同环境、不同数据量可能也不同)。
看来,MySQL优化器还是有必要进一步提高的哟 :)
测试使用版本:GreatSQL 8.0.25
(MySQL 5.6.39结果亦是如此)。
Enjoy MySQL :)
《实战MGR》视频课程
戳此小程序即可直达B站
或复制链接在浏览器中打开
https://space.bilibili.com/1363850082
文章推荐:
面向金融级应用的GreatSQL正式开源
GreatSQL重磅特性,InnoDB并行查询优化测试
技术分享|在Ubuntu下编译安装GreatSQL
《叶问》37期,三节点的MGR集群关掉两个节点后还能继续读写吗
MySQL SQL 优化参数 引发的悲剧
SQL案例分析之部分查询和全部查询
一周碎碎念,2021.11.7,两个MGR集群间还可以构建传统的主从复制通道吗
Percona XtraBackup 8.0.26实战大全
技术分享 | Update更新慢、死锁等问题的排查思路分享
在Linux下源码编译安装GreatSQL/MySQL
点击文末“阅读原文”直达老叶专栏
MySQL为什么错误选择代价更大的索引相关推荐
- 想突破现状,就得付出更大的努力!
1.最难突破的就是父母 父母是每个人的起点,也是绝大部分人的天花板.中国最近这代人大部分都比父母混的强,是因为父母被耽搁了,再往后几代,大家就能看出来这句话的威力了,美国英国德国那边的成熟型社会这一点 ...
- 【MySQL高级篇】第06章_索引的数据结构
第06章_索引的数据结构 1. 为什么使用索引 索引是存储引擎用于快速找到数据记录的一种数据结构,就好比一本教科书的目录部分,通过目录中找到对应文章的页码,便可快速定位到需要的文章.MySQL中也是一 ...
- MySQL的order by该如何避免“未命中索引“
不少同学私信我说,用Explain查看Order By语句执行计划时经常发现用不上索引,难道花好多时间和资源创建的联合索引都摆烂了?今天我把几个同学遇到的情况整理出来,做一个Order By使用索 ...
- MySQL的order by该如何避免未命中索引
在使用Explain查看Order By语句执行计划时经常发现用不上索引,难道花好多时间和资源创建的联合索引都摆烂了?今天我把遇到的情况整理出来,做一个Order By使用索引的坑点分享.希望对你有用 ...
- mysql负载均衡分区_分区和负载均衡让MySQL更大更好
通常,当我们的MySQL数据库逐渐变慢时,我们就希望通过一切努力使它变得更快.更强.更大.更好!那么都有哪些方法呢?别着急,我会一个一个给大家介 绍如何才能实现这些美好的愿望.阅读本系列文章将有助于扩 ...
- 任正非:为什么华为选择与西工大合作,而没选清华北大,mysql连接查询原理
西工大同清北等13所高校一样,在今年的5月22日被美国商务部列入实体名单,同时被限制使用美国的相关正版软件,而且很多之前和美国的合作也终止,不能用一些软件,同时也不能购买许多的美国重要零部件. 导致西 ...
- 计算机科学与技术专业歧视女生吗,考研选择这3大专业,女生就业不比男生差,反而女生更容易被看中...
原标题:考研选择这3大专业,女生就业不比男生差,反而女生更容易被看中 学有方法,考有技巧,优学优考策略致力于学与考的最优结合,助力学子考入理想大学! 一年一度的高考填报志愿又来临了.在这惊心动魄,议论 ...
- 巨杉数据库 CTO 王涛:区块链+数据库,底层技术融合是否带来更大爆发?
巨杉数据库 CTO 王涛:区块链+数据库,底层技术融合是否带来更大爆发? 4 月 22 日,在掘金技术社区主办的沙龙上,巨杉数据库 CTO 王涛以<区块链技术 VS 数据库技术:颠覆还是融合&g ...
- 在MySQL中当有多个索引时 你知道MySQL是如何选择索引的吗 ???
在码农的世界里,优美的应用体验,来源于程序员对细节的处理以及自我要求的境界,年轻人也是忙忙碌碌的码农中一员,每天.每周,都会留下一些脚印,就是这些创作的内容,有一种执着,就是不知为什么,如果你迷茫,不 ...
最新文章
- go map的定义和使用 键值对存储
- 加载场景不销毁的实现
- 深入理解this和call、bind、apply对this的影响及用法
- Alpha 通道的概念与功能
- pip show pip可以查看pip的版本以及升级pip到最新版本
- 【APIO2015】完跪记
- 批处理-删除环境变量
- NET开发资源站点和部分优秀.NET开源项目
- 20200122每日一句
- 在UITextView显示HTML,以及NSAttributedString乱码问题解决 swift
- 服务器系统如何校验md5值,什么是md5校验工具,md5校验工具怎么用?
- Python入门经典. 以解决计算问题为导向的Python编程实践
- python 工具变量_工具变量读书笔记
- 用路由器打印机显示服务器不存在,打印机连路由器怎么搜索不到
- centos离线安装docker-ce 18.03.0-ce
- Java第一天笔记01——jdk8的安装与环境变量的配置
- 知乎 | 给博士一年级新生的建议!
- 计算机课学生评价用语,关于学生上课的评语及评课用语
- 【项目篇-项目选题与类型】创赛项目来源、项目选题建议;四种常见类型项目各自内容的侧重点与区别
- [日推荐]『youhui优惠券』领点优惠券再去买买买!
热门文章
- 手写ngIf,使用视图容器引用ViewContainerRef和模板引用TemplateRef
- jqGrid 设置单元格行高的方法
- 在Word中插入x拔字符
- 大连python培训费用-大连Python培训
- Figures now render in the Plots pane by default. To make them also appear inline in the Console, unc
- TR-069协议介绍
- 基于STM32和GPS-NEO-6M模块实现GPS导航定位的
- 云台和华为p30pro_p30pro和mate30pro对比
- 中国石油大学《马克思主义基本原理概论#》第一阶段在线作业
- oracle vsm5,傲里拔尊--宝马M5 VS.奔驰CLS55AMG