MySQL STRAIGHT_JOIN

问题

最近在调试一条查询耗时5s多的sql语句,这条sql语句用到了多表关联(inner join),按时间字段排序(order by),时间字段上已经创建了索引(索引名IDX_published_at)。通过explain分析发现,时间字段上的索引没用上(Using temporary和Using filesort),问题很明显,但是原因是什么呢?

SELECT * FROM news n0_ inner join news_translations n1_ ON n0_.id = n1_.translatable_id inner join channels_news c3_ ON n0_.id = c3_.news_id
WHERE
((n0_.unpublished_at IS NOT NULL AND (CURRENT_TIMESTAMP >= n0_.published_at AND CURRENT_TIMESTAMP < n0_.unpublished_at)) OR (CURRENT_TIMESTAMP >= n0_.published_at AND n0_.unpublished_at IS NULL))
AND (n0_.status = 1 AND n0_.content_type_id = 1) AND n0_.id NOT IN (510466, 510433, 24, 11, 10, 9, 4)
AND n0_.home_position_id IS NULL
AND
n1_.locale = ‘zh_CN‘
AND
c3_.channel_id = 1
ORDER BY n0_.published_at DESC
LIMIT 5 ;

优化前sql语句

+-------+--------+-------------------------------+--------+-----------------------------------------------------------+
| table | type   | key                           | rows   | Extra                                                     |
+-------+--------+-------------------------------+--------+-----------------------------------------------------------+
| c3_   | ref    | IDX_87B9249E72F5A1AA          | 161590 | Using where; Using index; Using temporary; Using filesort |
| n0_   | eq_ref | PRIMARY                       |      1 | Using where                                               |
| n1_   | ref    | UNIQ_20FDB3302C2AC5D34180C698 |      1 | Using where                                               |
+-------+--------+-------------------------------+--------+-----------------------------------------------------------+

explain分析结果 有所删减

经过一轮折腾的优化,得到了下面的sql语句

SELECT * FROM news n0_ STRAIGHT_JOIN news_translations n1_ ON n0_.id = n1_.translatable_id STRAIGHT_JOIN channels_news c3_ ON n0_.id = c3_.news_id
WHERE
((n0_.unpublished_at IS NOT NULL AND (CURRENT_TIMESTAMP >= n0_.published_at AND CURRENT_TIMESTAMP < n0_.unpublished_at)) OR (CURRENT_TIMESTAMP >= n0_.published_at AND n0_.unpublished_at IS NULL))
AND (n0_.status = 1 AND n0_.content_type_id = 1) AND n0_.id NOT IN (510466, 510433, 24, 11, 10, 9, 4)
AND n0_.home_position_id IS NULL
AND
n1_.locale = ‘zh_CN‘
AND
c3_.channel_id = 1
ORDER BY n0_.published_at DESC
LIMIT 5 ;

优化后sql语句

+-------+--------+-------------------------------+--------+--------------------------+
| table | type   | key                           | rows   | Extra                    |
+-------+--------+-------------------------------+--------+--------------------------+
| n0_   | range  | IDX_published_at              | 255440 | Using where              |
| n1_   | ref    | UNIQ_20FDB3302C2AC5D34180C698 |      1 | Using where              |
| c3_   | eq_ref | PRIMARY                       |      1 | Using where; Using index |
+-------+--------+-------------------------------+--------+--------------------------+

优化后explain分析结果 有所删减

优化前后的变化有四点:1、不再Using temporary和Using filesort;2、表的查询顺寻变了;3、查询扫描的rows增加了;4、查询时间由5s降到了0.02s。

原因分析

优化前后出现的四点变化,性能显著提升,需要从mysql的关联的连接处理说起。

以下参考《高性能MySQL》

1)优化前的sql语句以channels_news为第一个关联表,找到161590条记录;2)优化后的sql语句以news表为第一关联表,找到255440条记录,比第一条sql语句查找多了9W多条。因此,优化前的sql语句的关联顺序是MySQL优化器的选择,可以让查询进行更小的嵌套循环和回溯操作。MySQL通过选择合适的关联顺序来让查询执行的成本尽可能低,重新定义关联的顺序是优化器很重要的一部分功能。不过有时候,优化器给出的并不是最优的关联顺序。这时可以使用STRAIGHT_JOIN关键字重写查询,让优化器按照你认为的最优关联顺序执行。

从优化后的explain分析结果看出,news是驱动表,结果以news表的published_at字段进行排序,所以用上了索引,避免了Using temporary和Using filesort,自然而然的,查询时间也降下来了。正如前面说的,mysql的优化器通过粗暴的小表驱动大表来选择连接的顺序,第一条sql语句扫描了161590行,第二条sql语句扫描了255440行,优化后的sql语句扫描的行数增加了。

结语

结案陈词:造成这次sql语句查询耗时5s的原因是,sql语句order by的字段不在mysql的优化器选在驱动表上,所以导致这次关联查询排序字段上的索引没有被使用。因此,通过使用STRAIGHT_JOIN来强制制定关联查询的表顺序,以达到优化的目的。但是,有时候我们人为地指定顺序不一定比mysql的优化引擎准确,所以在使用STRAIGHT_JOIN的时候三思而后行。

本文链接:http://www.hcoding.com/?p=211

原创文章,转载请注明:JC&hcoding.com

书愤

陆游

早岁那知世事艰,中原北望气如山。

楼船夜雪瓜洲渡,铁马秋风大散关。

塞上长城空自许,镜中衰鬓已先斑。

出师一表真名世,千载谁堪伯仲间。

时间: 2024-10-10 20:58:29

MySQL STRAIGHT_JOIN的相关文章

mysql的join操作

一.Join语法概述 join 用于多表中字段之间的联系,语法如下: ... FROM table1 INNER|LEFT|RIGHT JOIN table2 ON conditiona table1:左表:table2:右表. JOIN 按照功能大致分为如下三类: INNER JOIN(内连接,或等值连接):取得两个表中存在连接匹配关系的记录. LEFT JOIN(左连接):取得左表(table1)完全记录,即是右表(table2)并无对应匹配记录. RIGHT JOIN(右连接):与 LEF

Mysql Join语法解析与性能分析

一.Join语法概述 join 用于多表中字段之间的联系,语法如下: ... FROM table1 INNER|LEFT|RIGHT JOIN table2 ON conditiona table1:左表:table2:右表. JOIN 按照功能大致分为如下三类: INNER JOIN(内连接,或等值连接):取得两个表中存在连接匹配关系的记录. LEFT JOIN(左连接):取得左表(table1)完全记录,即使右表(table2)并无对应匹配记录. RIGHT JOIN(右连接):与 LEF

mysql数据库基础(一)

Mysql列子查询及IN,ALL,SOME,ANY的使用 table1: s1 2 10 table2: s2 5 12 20 列子查询中使用IN,ANY,SOME和ALL操作符. IN:在指定项内,同IN(项1,项2,..) ANY:与比较操作符联合使用,接在比较操作符后面表示与子查询返回的任何值比较为TRUE,则返回TRUE. SOME:ANY的别名,较少使用. ALL:与比较操作符联合使用,表示与子查询返回的所有值比较都为TRUE,则返回TRUE. 细节:IN 是 = ANY 的别名,二者

在mysql语句中为什么要加反引号

在MySQL语句中我们有时候经常会遇到反引号(``),刚开始的时候不知道什么意思,他是什么作用呢? Select * from `member` order by posts desc limit 0,10; 它是为了区分MYSQL的保留字与普通字符而引入的符号. 举个例子:SELECT `select` FROM `test` WHERE select='字段值' 在test表中,有个select字段,如果不用反引号,MYSQL将把select视为保留字而导致出错,所以,有MYSQL保留字作为

看懂mysql执行计划--官方文档

原文地址:https://dev.mysql.com/doc/refman/5.7/en/explain-output.html 9.8.2 EXPLAIN Output Format The EXPLAIN statement provides information about the execution plan for a SELECT statement. EXPLAIN returns a row of information for each table used in the S

MySQL索引原理及慢查询优化

原文:http://tech.meituan.com/mysql-index.html MySQL凭借着出色的性能.低廉的成本.丰富的资源,已经成为绝大多数互联网公司的首选关系型数据库.虽然性能出色,但所谓“好马配好鞍”,如何能够更好的使用它,已经成为开发工程师的必修课,我们经常会从职位描述上看到诸如“精通MySQL”.“SQL语句优化”.“了解数据库原理”等要求.我们知道一般的应用系统,读写比例在10:1左右,而且插入操作和一般的更新操作很少出现性能问题,遇到最多的,也是最容易出问题的,还是一

查询优化 | MySQL慢查询优化

?Explain查询:rows,定位性能瓶颈. 只需要一行数据时,使用LIMIT1. 在搜索字段上建立索引. 使用ENUM而非VARCHAR. 选择区分度高的列作为索引. 采用扩展索引,而不是新建索引. 慢查询日志:log-slow-queries,mysqldumpslow工具. 避免select * 尽可能使用NOT NULL where中避免索引无效. <MySQL索引原理及慢查询优化>    http://tech.meituan.com/mysql-index.html [查询语句的

MySQL导入csv数据

在工作中经常遇到需要往MySQL数据库中导入开发人员提供的数据,其中一种是csv格式的,导入方式如下: 创建相应的表格 mysql> use kevin; Database changed mysql> CREATE TABLE GeoPC_Places (     -> ISO varchar(2) NOT NULL,     -> Country varchar(50) NOT NULL,     -> Language varchar(2) NOT NULL,     

mysql命令

1 添加mysql\bin 到path 2 启动cmd 登陆 :mysql -hlocalhost -uroot -pxxxx 3退出 exit 在mysql>提示符下命令   show databases; 显示数据库 use  world; 打开world数据库 show tables; 显示表 create database 库名;建库 drop database 库名 ;删库 create table ...;建表 drop table 表名;删表 delete from 表名; 删除记