关于两张3亿数据表的关联查询优化

话不多说,上表:
factor_status_log_to_reason(3亿条数据):

CREATE TABLE `factor_status_log_to_reason` (
  `fsl_id` int(11) unsigned NOT NULL,
  `reason_id` int(11) unsigned NOT NULL,
  PRIMARY KEY (`fsl_id`,`reason_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8

factor_status_log(2亿条数据):

CREATE TABLE `factor_status_log` (
  `fsl_id` int(11) unsigned NOT NULL AUTO_INCREMENT,
  `object_key` varchar(255) NOT NULL,
  `operator_name` varchar(32) NOT NULL,
  `operation_id` int(11) unsigned NOT NULL,
  `products_id` int(11) unsigned NOT NULL,
  `mc_id` int(11) unsigned NOT NULL,
  `object_status` int(11) DEFAULT NULL,
  `date_added` timestamp NOT NULL DEFAULT '1999-09-09 00:00:00',
  PRIMARY KEY (`fsl_id`),
  KEY `idx_objk` (`object_key`),
  KEY `idx_pm` (`products_id`,`mc_id`),
  KEY `idx_date` (`date_added`)
) ENGINE=InnoDB AUTO_INCREMENT=343169419 DEFAULT CHARSET=utf8

不要在意为啥一个表这么多数据,因为他就是有这么多数据,没有分库分表

需求:找到reason_id为64,67的产品id以及相关数据。

第一版sql:

select * from factor_status_log fsl join factor_status_log_to_reason fsltr on fsltr.fsl_id = fsl.fsl_id and fsltr.reason_id in (67,64) limit 10;

执行之后发现巨慢,用explain查看执行计划如下:

id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE fsl ALL PRIMARY NULL NULL NULL 207636393
1 SIMPLE fsltr ref PRIMARY PRIMARY 4 products_center_v1.fsl.fsl_id 1 Using where; Using index

不理解为啥一个表没有走索引,过滤条件都是加索引的呀。

接下来进入了短暂的捋原理阶段:
两个表链接的原理是这样的,如果是外连接,那么可以自己选定一张表作为驱动表,如果是内连接,MySQL会选定一张表作为驱动表。比如以上 我写的sql是内连接,那个表是驱动表呢?看explain第一条的表fsl就是驱动表。
选定驱动表之后,会对驱动表进行单表扫描,扫描出结果。比如扫描出两条数据fsl_id = 1,2。
然后才能利用到关联条件fsltr.fsl_id = fsl.fsl_id,对被驱动表进行两次查找,也就是fsltr.fsl_id = 1,fsltr.fsl_id = 2。如果驱动表找出一万条数据,那么被驱动表就需要查找一万次。有没有感觉到其实这就像是一个嵌套循环?对滴,这种查找就叫循环嵌套查询。

原理知道了,为啥咱写的说sql慢呢?根本原因就是在MySQL首先对驱动表factor_status_log进行单表查询的时候,并没有对其用索引条件进行过滤,就相当于

select * from factor_status_log;

并不会走索引,所以是全表扫描。

优化:

select * from factor_status_log_to_reason fsltr ,factor_status_log fsl where fsl.date_added < '2019-01-07 00:00:00' and fsl.date_added > '2018-06-07 00:00:00' and fsltr.fsl_id = fsl.fsl_id and fsltr.reason_id in (67,64) limit 10;

目的是对驱动表用索引date_added进行过滤一下。执行计划如下:

id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE fsl range PRIMARY,idx_date idx_date 4 NULL 61972696 Using where
1 SIMPLE fsltr ref PRIMARY PRIMARY 4 products_center_v1.fsl.fsl_id 1 Using where; Using index

可以看到单表扫描是range,并且走了索引idx_date。

可以在几秒之内出结果了。

在这里插入图片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章