mysql半连接_mysql表的半连接,反连接导致的mysql性能优化剖析
[导读] 关于Oracle的半连接,反连接,我一直认为这是一个能讲很长时间的话题,所以在我的新书《Oracle DBA工作笔记》中讲性能优化的时候,我花...
关于Oracle的半连接,反连接,我一直认为这是一个能讲很长时间的话题,所以在我的新书《Oracle DBA工作笔记》中讲性能优化的时候,我花了不少的笔墨做了阐述,结果在做MySQL性能优化的时候,优化思路切换到MySQL层面,我发现要说的东西要更多。总体来看,这部分的优化细节MySQL还在路上,不同的版本中都能够一窥其中的变化,可以看到在不断改进。
在表的连接上,半连接,反连接本身很平常,但是统计信息的不够丰富导致执行计划的评估中可能会出现较大差别,会很可能把半连接,反连接的实现方式和执行路径的差异放大,导致SQL性能变差,同时MySQL里面in和exists的差距也在减小。
我就简化一下我的描述,拿MySQL 5.6版本的一些差别来说明。算是对5.5和5.7的承上启下。
我们创建一个表t_fund_info,数据量在两百万,创建另外一个表t_user_login_record数据量和t_fund_info一样。 t_fund_info有主键字段account,t_user_login_record没有索引。
SQL语句如下:select account
from t_fund_info
where money >= 300
and account not in (select distinct (account)
from t_user_login_record
where add_time >= "2016-06-01");
里面的列select_type PRIMARY代表子查询中的最外层查询,此处不是主键查询。而SUBQUERY代表是子查询内层查询的第一个SELECT,结果不会依赖于外部查询的结果集。
从type为ALL代表是全表扫描,所以这样一个查询两个表都是全表扫描,在MySQL内部解析的时候是怎么分解的呢。我们通过explain extended的方式来得到更详细的信息。/* select#1 */
select test . t_fund_info . account AS account
from test . t_fund_info
where ((test . t_fund_info . money >= 300) and
(not (
(test . t_fund_info . account, test . t_fund_info .
account in
(
( /* select#2 */
select test . t_user_login_record . account
from test . t_user_login_record
where (test . t_user_login_record . add_time >= "2016-06-01")), <
primary_index_lookup >
(test . t_fund_info . account in
table > on
where((test . t_fund_info . account = materialized - subquery .
account))))))))
可以看到启用了临时表,查取了子查询的数据作为后续的缓存处理数据.
这样的处理,究竟对性能提升有多大呢,其实不大,而且性能改进也很有限。
我们换一个思路,那就是使用not existsexplain extended select t1.account from t_fund_info t1 where t1.money
>=300 and not exists (select distinct(t2.account) from
t_user_login_record t2 where t1.account=t2.account and t2.add_time
>="2016-06-01");这种方式在MySQL是如何分解的呢。
select test . t1 . account AS account
from test . t_fund_info t1
where ((test . t1 . money >= 300) and
(not
(exists ( /* select#2 */
select test . t2 . account
from test . t_user_login_record t2
where ((test . t1 . account = test . t2 . account) and
(test . t2 . add_time >= "2016-06-01"))))))
可以看到几乎没有做什么特别的改动。
这一点在5.5,5.6,5.7中都是很相似的处理思路。
当然这种方式相对来说性能提升都不大。一个局限就在于统计信息不够丰富,所以自动评估就会出现很大的差距。
这个地方我们稍放一放,我们添加一个索引之后再来看看。create index ind_account_id2 on t_user_login_record(account);
然后使用not in的方式查看解析的详情。select test . t_fund_info . account AS account
from test . t_fund_info
where ((test . t_fund_info . money >= 300) and
(not (
(test . t_fund_info .
account,
(
( (test . t_fund_info . account) in t_user_login_record on
ind_account_id2
where((test . t_user_login_record . add_time >= "2016-06-01") and
( (test . t_fund_info . account) = test .
t_user_login_record . account))))))))
可以看到这个方式有了索引,not in和not exits的解析方式很相似。有一个差别就是在子查询外有了的处理方式。
我们来看看两者的差别,同样的步骤,有了索引之后,估算的key_len(使用索引的长度)为182,估算行数为1-----------------+---------+------+---------
key | key_len | ref | rows
-----------------+---------+------+---------
NULL | NULL | NULL | 1875524
ind_account_id2 | 182 | func | 1
而之前没有索引的时候,这个结果差别就很大了,是190多万。------+---------+------+---------
key | key_len | ref | rows
------+---------+------+---------
NULL | NULL | NULL | 1875524
NULL | NULL | NULL | 1945902
而顺带看看有了索引之后,not exists的方式是否会有改变。/* select#1 */
select test . t1 . account AS account
from test . t_fund_info t1
where ((test . t1 . money >= 300) and
(not
(exists ( /* select#2 */
select test . t2 . account
from test . t_user_login_record t2
where ((test . t1 . account = test . t2 . account) and
(test . t2 . add_time >= "2016-06-01"))))))
以上可以看出,和没有添加索引的解析方式没有差别。哪里会差别呢,就是执行的估算行数上,有天壤之别。
所以通过这样一个反连接的小例子,可以看出来存在索引的时候,not in会内部转换为not exists的处理方式,而not exists的方式在存在索引和不存在,两者通过执行计划可以看出很大的差别,其中的一个瓶颈点就在于估算的行数。
mysql半连接_mysql表的半连接,反连接导致的mysql性能优化剖析相关推荐
- 解决使用Navicat等工具进行连接登录mysql的1130错误,无法使用Ip远程连接的问题(mysql为8.0版本)
解决使用Navicat等工具进行连接登录mysql的1130错误,无法使用Ip远程连接的问题(mysql为8.0版本) 参考文章: (1)解决使用Navicat等工具进行连接登录mysql的1130错 ...
- 【mysql】连表查询(内连接,左连接,右连接,全外连接)
连表查询(内查询,左查询,右查询,全外查询) 说明 正文 创建表单 1:创建部门表:department 2:创建员工表:employee 内连接 左连接 右连接 全外连接 连表查询(内查询,左查询, ...
- 【MySQL】多表联合查询、连接查询、子查询
文章目录 [1]连接查询 内连接查询 外连接查询 左连接 右连接 [2]联合查询 [3]子查询 带in关键字的子查询 带比较运算符的子查询 带exists的子查询 带any关键字的子查询 带all关键 ...
- nhibernate mysql配置_MySql(一)_利用NHibernate和MySql交互
1.基础配置,添加MySql和nHibernate的引用 (1) 添加引用,导入MySql.data.dll: 利用MySql提供的API操作: (2) 添加引用,导入NHibernate.d ...
- mysql怎么修改表的列名字_怎么修改mysql的表名和列名
怎么修改mysql的表名和列名 在mysql中,可以通过"ALTER TABLE 旧表名 RENAME 新表名;"语句来修改表名,通过"ALTER TABLE 表名 CH ...
- mysql慢查询 表级锁_三分钟了解Mysql的表级锁——《深究Mysql锁》
延伸阅读: 五分钟了解Mysql的行级锁 一分钟深入Mysql的意向锁 mysql锁相关讲解及其应用--<深究mysql锁>了解锁前,一定要先看这篇,了解什么是MVCC,如果我们学习锁,没 ...
- mysql删除分表键_一文看懂 MySQL 分区和分表,提高表增删改查效率
原标题:一文看懂 MySQL 分区和分表,提高表增删改查效率 作者:冯帅,精通Oracle. MySQL. 擅长异构数据库数据同步及迁移.数据库的设计和调优,对高可用方案有深入研究. MySQL分区和 ...
- mysql怎么修改表的列名字_怎么修改mysql的表名和列名?
在mysql中,可以通过"ALTER TABLE 旧表名 RENAME 新表名;"语句来修改表名,通过"ALTER TABLE 表名 CHANGE 旧字段名/列名 新字段 ...
- 存放在mysql数据库的表_下列选项中,存放在mysql数据库的表是
下列选项中,存放在mysql数据库的表是 答:column_priv db user tables_priv 关于礼说法正确的是(). 答:对人有约束力 金钱能买来熟人,买不来朋友 答:√ 绿泥石强度 ...
最新文章
- Android屏幕像素密度适配详解
- HDU3657Game(最大流)
- 'unicodeescape' codec can't decode bytes in position 16-17: malformed \N character escape
- javase基础第三天
- BZOJ 3173: [Tjoi2013]最长上升子序列
- 关于几种编码的那些事
- 前端学习(2439):jsonbin-init的使用
- STM32F7xx —— 看门狗
- [图]运动鞋用“囧”字做图案卖断货
- DIV实现CSS 的placeholder效果
- 对select标签中的option默认选中后端的数据
- 代数拓扑\集合拓扑\代数拓扑\拓扑关系\拓扑结构_笔记
- TensorFlow Session 中关于 GPU 的配置项解析 ——转自 慢慢学TensorFlow 微信公众号
- 家政上门预约服务小程序源码+前端后端
- 华为鸿蒙11公测版,首升鸿蒙2.0系统!华为官宣这10款机型率先公测EMUI11-互联网/电商-文章-小虾米...
- win10+anaconda+pycharm python画图完整过程
- OPPO A96 参数配置
- svn incoming内容无法更新下来,且提交报错:svn: E155015: Aborting commit: XXX remains in conflict
- UCSD异常检测数据集
- python做马尔科夫模型预测法_通过Python的Networkx和Sklearn来介绍隐性马尔科夫模型...