Mysql
儘管有索引,但帶有額外標準的 PK 內部連接很慢?
鑑於下面的兩個表格,我很難理解:
- 為什麼即使前兩個查詢很快,第三個查詢也很慢
- EXPLAIN 到底在說什麼
- 我可以做些什麼來顯著加快慢查詢?
在 PK 上加入兩個表很快:
mysql> select sql_no_cache p.id, sv.postProcessed from product_views p, site_visits sv where p.siteVisitId=sv.id limit 1; +----+---------------+ | id | postProcessed | +----+---------------+ | 1 | 1 | +----+---------------+ 1 row in set (0.10 sec)
所以只是按時間戳範圍選擇 PV:
mysql> select sql_no_cache p.id, p.timestamp from product_views p where p.timestamp >= "2012-10-10" and p.timestamp < "2012-11-10" limit 1; +-----------+---------------------+ | id | timestamp | +-----------+---------------------+ | 501719231 | 2012-10-10 00:01:03 | +-----------+---------------------+ 1 row in set (0.56 sec)
但加入兩者真的很慢(需要 5 min+ ):
mysql> select sql_no_cache p.id, p.timestamp, sv.postProcessed from product_views p, site_visits sv where p.siteVisitId=sv.id and p.timestamp >= "2012-10-10" and p.timestamp < "2012-11-10" limit 1;
這是解釋
mysql> explain select sql_no_cache p.id, p.timestamp, sv.postProcessed from product_views p, site_visits sv where p.siteVisitId=sv.id and p.timestamp >= "2012-10-10" and p.timestamp < "2012-11-10" limit 1; +----+-------------+-------+--------+------------------------------------------------------------+--------------------+---------+---------------------+-----------+--------------------------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+-------------+-------+--------+------------------------------------------------------------+--------------------+---------+---------------------+-----------+--------------------------+ | 1 | SIMPLE | p | index | FK52C29B1E3CAB9CC4,timestamp_idx,siteVisitId_timestamp_idx | FK52C29B1E3CAB9CC4 | 8 | NULL | 119195469 | Using where; Using index | | 1 | SIMPLE | sv | eq_ref | PRIMARY | PRIMARY | 8 | clabs.p.siteVisitId | 1 | | +----+-------------+-------+--------+------------------------------------------------------------+--------------------+---------+---------------------+-----------+--------------------------+ 2 rows in set (0.10 sec)
問題
- 我希望最後一個查詢的執行速度與前兩個加在一起的速度差不多:1)在給定的時間戳內辨識 product_view 和 2)對匹配的 site_visit 行進行持續查找。在該時間戳範圍內 product_views 中有 < 95m 行,不確定為什麼要掃描 120M…
- 上面的解釋似乎說沒有使用“timestamp_idx”。為什麼不?(我猜 mysqld 正在對按時間戳匹配的 product_views 進行全分區掃描)
- 我嘗試添加一個(siteVisitId,timestamp)索引來覆蓋“WHERE”中使用的所有屬性——但這也沒有被使用。為什麼?
- 我能做些什麼來加快速度?
關於我們的數據庫的注意事項:
- 兩個表都是 100M+ 行
- 每個 product_view 都只有一個 siteVisit。(FK 被移除以適應 InnoDB 分區約束)
- 使用 mysql 5.5
- 沒有針對數據庫伺服器的其他流量
表格
mysql> show create table site_visits\G *************************** 1. row *************************** Table: site_visits Create Table: CREATE TABLE `site_visits` ( `id` bigint(20) NOT NULL AUTO_INCREMENT, `postProcessed` tinyint(1) NOT NULL, `siteVisitState` varchar(255) DEFAULT NULL, PRIMARY KEY (`id`), KEY `post_processed_idx` (`postProcessed`), KEY `visit_state_idx` (`siteVisitState`) ) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=latin1 mysql> show create table product_views\G *************************** 1. row *************************** Table: product_views Create Table: CREATE TABLE `product_views` ( `id` bigint(20) NOT NULL AUTO_INCREMENT, `timestamp` datetime NOT NULL, PRIMARY KEY (`id`,`timestamp`), KEY `FK52C29B1E3CAB9CC4` (`siteVisitId`), KEY `rebateSearchZipCode_idx` (`rebateSearchZipCode`), KEY `siteVisitId_timestamp_idx` (`siteVisitId`,`timestamp`), KEY `timestamp_idx` (`timestamp`) ) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=latin1 /*!50500 PARTITION BY RANGE COLUMNS(`timestamp`) (PARTITION p0 VALUES LESS THAN ('2012-05-01') ENGINE = InnoDB, PARTITION p1 VALUES LESS THAN ('2012-06-01') ENGINE = InnoDB, PARTITION p2 VALUES LESS THAN ('2012-07-01') ENGINE = InnoDB, PARTITION p3 VALUES LESS THAN ('2012-08-01') ENGINE = InnoDB, /* partition declarations truncated */ PARTITION p33 VALUES LESS THAN (MAXVALUE) ENGINE = InnoDB) */
優化器看不到您的條件是相關的,因此選擇了錯誤的訪問方法。
基本上,它考慮了兩種選擇:
- 掃描索引
siteVisitId
直到第一個匹配site_visits
和第一個滿足timestamp
條件。- 掃描索引
timestamp
直到第一個匹配site_visits
。由於
timestamp
是主鍵的一部分而siteVisitId
不是,第二個計劃將涉及product_views
比純索引掃描慢幾倍的表查找(Using index
計劃中的註釋)。優化器計算滿足條件的條件機率
timestamp
(假設存在相應的site_visit
記錄)並將其與表訪問的成本進行比較。由於您的
timestamp
條件非常廣泛(如索引直方圖所示),優化器更喜歡第一種方法。然而,由於兩者
siteVisitId
都是timestamp
增量的,它們是相關的,並且兩個匹配的條件機率不僅僅是它們獨立機率的乘積。簡而言之,您必須過濾許多低點
siteVisitId
,直到找到第一個匹配項timestamp
,這正是您的查詢發生的情況。您應該添加
ORDER BY timestamp
到查詢中以使timestamp
索引更便宜,因為它不必排序。它還有助於timestamp, siteVisitId
(按此順序)創建索引以避免表查找。