關於兩張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。

可以在幾秒之內出結果了。

在這裏插入圖片描述

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