mysql(二) 慢查询分析(一)

时间:2021-11-21 07:11:07

如下表结构:

CREATE TABLE `trade_order` (
  `order_id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT '订单编号',
  `total_price` bigint(20) DEFAULT NULL COMMENT '订单总价',
  `item_name` varchar(128) DEFAULT NULL COMMENT '商品名称',
  `mobile` varchar(16) DEFAULT NULL COMMENT '下单电话',
  `gmt_create` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
  `gmt_modify` timestamp NULL DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP,
  PRIMARY KEY (`order_id`)
) ENGINE=InnoDB AUTO_INCREMENT=239 DEFAULT CHARSET=utf8mb4;
CREATE TABLE `trade_sub_order` (
  `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
  `order_id` bigint(20) DEFAULT NULL COMMENT '订单号',
  `item_price` bigint(20) DEFAULT NULL COMMENT '商品单价',
  `item_nums` int(11) DEFAULT NULL COMMENT '商品数量',
  `gmt_create` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
  `gmt_modify` timestamp NULL DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP,
  PRIMARY KEY (`id`),
  KEY `order_index` (`order_id`)
) ENGINE=InnoDB AUTO_INCREMENT=1227 DEFAULT CHARSET=utf8mb4;

在执行联合查询时,如下2种写法的执行计划有明显差别。

第一种:

select * from trade_order left join `trade_sub_order` using(`order_id`) order by `trade_order`.`order_id` desc;

第二种:

select * from trade_order left join `trade_sub_order` using(`order_id`) order by `trade_sub_order`.`order_id` desc;

两者的差别在于order by的表字段不同。

看执行计划:

第一种的执行计划:

mysql(二) 慢查询分析(一)

第二种的执行计划:

mysql(二) 慢查询分析(一)

rows的差别忽略,因为数据在不停的新增过程中。

第一种方式有主键索引,不需要使用临时表。第二种全表扫描,使用临时表,使用文件排序。

这里面的关键问题在于,MySQL 表关联的算法是“Nest Loop Join”,是通过驱动表的结果集作为循环基础数据,然后一条一条地通过该结果集中的数据作为过滤条件到下一个表中查询数据,然后再合并结果(临时表)。

在explain中,第一行出现的表就是驱动表,对于驱动表字段的排序可以直接进行,对于非驱动表字段的排序需要对循环查询的合并结果(临时表)进行排序。所以在第二种写法下就会出现Using temporary。而且由于对非驱动表字段排序,导致将驱动表的全表数据作为驱动表的结果集,产生全表扫描,无法使用到主键索引。

所以,在explain中,有Using temporary时,需要关注是否使用非驱动表字段做排序处理。

 

驱动表的定义:

在进行多表连接查询时,

  • 指定了连接条件,满足查询查询条件的记录数少的表为驱动表;
  • 未指定连接条件,行数少的表为驱动表;