关于两张3亿数据表的关联查询优化
Posted 孙晓凯
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了关于两张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。
可以在几秒之内出结果了。
以上是关于关于两张3亿数据表的关联查询优化的主要内容,如果未能解决你的问题,请参考以下文章