关于两张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查看执行计划如下:

idselect_typetabletypepossible_keyskeykey_lenrefrowsExtra
1SIMPLEfslALLPRIMARYNULLNULLNULL207636393
1SIMPLEfsltrrefPRIMARYPRIMARY4products_center_v1.fsl.fsl_id1Using 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进行过滤一下。执行计划如下:

idselect_typetabletypepossible_keyskeykey_lenrefrowsExtra
1SIMPLEfslrangePRIMARY,idx_dateidx_date4NULL61972696Using where
1SIMPLEfsltrrefPRIMARYPRIMARY4products_center_v1.fsl.fsl_id1Using where; Using index

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

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

以上是关于关于两张3亿数据表的关联查询优化的主要内容,如果未能解决你的问题,请参考以下文章

MYSQL中两张表,怎么使用关联查询?

Oracle数据库,关于关联两张表更新问题

oracle 想把三张表关联起来,怎么关联?

MySQL 对于千万级的大表要怎么优化

Oracle 查询优化的基本准则详解

sql两张表(主表和字典表)关联查询,字典项翻译问题