2017-08-10 131 views
1

我有一個包含1,019,502條記錄和一個需要1.6秒運行的特定查詢的表。如果可能,我想盡量減少運行時間。MySQL ISAM搜索優化

該表是INNODB在MySQL 5.7(在Ubuntu):

mysql> describe summary_data; 
+--------------+------------------+------+-----+---------+-------+ 
| Field  | Type    | Null | Key | Default | Extra | 
+--------------+------------------+------+-----+---------+-------+ 
| propId  | int(10) unsigned | NO | PRI | NULL |  | 
| elemType  | varchar(50)  | NO | PRI | NULL |  | 
| sku   | varchar(100)  | NO | PRI | NULL |  | 
| family  | varchar(100)  | NO | PRI | NULL |  | 
| subcategory | varchar(100)  | NO | PRI | NULL |  | 
| category  | varchar(100)  | NO | PRI | NULL |  | 
| details  | varchar(255)  | YES |  | NULL |  | 
| merchSales | float(12,2)  | YES |  | NULL |  | 
| orders  | int(10) unsigned | YES |  | NULL |  | 
| quantity  | int(10) unsigned | YES |  | NULL |  | 
| margin  | float(12,2)  | YES |  | NULL |  | 
| grossSales | float(12,2)  | YES |  | NULL |  | 
| discount  | float(12,2)  | YES |  | NULL |  | 
| shipping  | float(12,2)  | YES |  | NULL |  | 
| tax   | float(12,2)  | YES |  | NULL |  | 
| createDate | datetime   | YES |  | NULL |  | 
| date   | date    | NO | PRI | NULL |  | 
| dateType  | varchar(10)  | NO | PRI | NULL |  | 
+--------------+------------------+------+-----+---------+-------+ 

查詢是如下:

SET @propId = 1, 
@from = '2016-01-01', 
@to = '2016-12-31', 
@elemType = 'sku', 
@sku = NULL, 
@family = NULL, 
@subcategory = NULL, 
@category = NULL; 

SELECT SUM(ifnull(merchSales,0)+ifnull(discount,0)) as totalSales 
,SUM(ifnull(merchSales,0)) as merchSales 
,SUM(ifnull(orders,0)) as orders 
,SUM(ifnull(quantity,0)) as quantity 
,sum(ifnull(grossSales,0)) as grossSales 
,sum(ifnull(discount,0))*(-1) as discount 
,sum(ifnull(shipping,0)) as shipping 
,elemType 
,sku 
,family 
,category 
,subcategory 
,details 
,SUM(ifnull(margin,0)) as margin 
,sum(ifnull(margin,0))/sum(ifnull(merchSales,0))*100 as marginPerc 
,SUM(ifnull(grossSales,0))/SUM(ifnull(orders,0)) as avgOrderVal 
,sum(ifnull(merchSales,0)+ifnull(discount,0))/sum(ifnull(margin,0))*100 as marginPercTotal 
FROM summary_data 
WHERE propId = @propId 
AND dateType = 'day' 
AND elemType = @elemType 
AND (@sku IS NULL OR sku = @sku) 
AND (@family IS NULL OR family = @family) 
AND (@subcategory IS NULL OR subcategory = @subcategory) 
AND (@category IS NULL OR category = @category) 
GROUP BY category,subcategory,family,sku 
ORDER BY merchSales DESC; 

由查詢中所使用的指數:

mysql> show indexes from summary_data; 
+--------------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+ 
| Table  | Non_unique | Key_name | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment | 
+--------------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+ 
| summary_data |   0 | PRIMARY |   1 | propId  | A   |   218 |  NULL | NULL |  | BTREE  |   |    | 
| summary_data |   0 | PRIMARY |   2 | elemType | A   |  1529 |  NULL | NULL |  | BTREE  |   |    | 
| summary_data |   0 | PRIMARY |   3 | category | A   |  5528 |  NULL | NULL |  | BTREE  |   |    | 
| summary_data |   0 | PRIMARY |   4 | subcategory | A   |  11198 |  NULL | NULL |  | BTREE  |   |    | 
| summary_data |   0 | PRIMARY |   5 | family  | A   |  15678 |  NULL | NULL |  | BTREE  |   |    | 
| summary_data |   0 | PRIMARY |   6 | sku   | A   |  17470 |  NULL | NULL |  | BTREE  |   |    | 
| summary_data |   0 | PRIMARY |   7 | dateType | A   |  17470 |  NULL | NULL |  | BTREE  |   |    | 
| summary_data |   0 | PRIMARY |   8 | date  | A   |  985490 |  NULL | NULL |  | BTREE  |   |    | 

查詢使用1,019,502條記錄中的約115,000條記錄。結果返回2106個聚合行。

任何意見將不勝感激!

*****編輯*****

添加說明:

+----+-------------+--------------+------------+------+----------------------------------+---------+---------+-------------+--------+----------+----------------------------------------------+ 
| id | select_type | table  | partitions | type | possible_keys     | key  | key_len | ref   | rows | filtered | Extra          | 
+----+-------------+--------------+------------+------+----------------------------------+---------+---------+-------------+--------+----------+----------------------------------------------+ 
| 1 | SIMPLE  | summary_data | NULL  | ref | PRIMARY,propId_4,propId_5,propId | PRIMARY | 156  | const,const | 492745 | 10.00 | Using where; Using temporary; Using filesort | 
+----+-------------+--------------+------------+------+----------------------------------+---------+---------+-------------+--------+----------+----------------------------------------------+ 
+0

我可能是錯的,因爲我還沒有做任何研究,但我相信空值的總和默認爲0。 – jhpratt

+0

解釋輸出說什麼?請將其添加(以文本形式)到您的問題中。 (建議您始終爲優化執行此操作) –

+0

及以下內容來自@jhpratt SUM()忽略NULL,因此您可以通過顛倒函數序列來避免在每行上運行IFNULL()。 IFNULL(SUM(column),0) –

回答

0

你的唯一不變的部分where子句包括:

WHERE propId = @propId 
AND dateType = 'day' 
AND elemType = @elemType 

所以有MIGHT在聲明涉及這3個字段的非唯一組合索引時有一些優勢propid,elemtype,datetype (nb:我不確定我可以在這樣的索引指定這些列的順序,它可能需要一些experimmentation),我會嘗試定義這樣一個指數後的解釋,但要確保這些變量保持NULL,而試運行這樣的:

@sku = NULL, 
@family = NULL, 
@subcategory = NULL, 
@category = NULL 

如果有現在嘗試使這4個變量中的任何一個非空。對你的解釋計劃有什麼影響?您可能會發現您需要在每個列上分別使用非唯一索引來幫助支持where子句的可變性。

即當您更改變量時,解釋計劃也會有所不同。

但是:在大於100萬行的1.6秒內,您將進入收益遞減的境界。

+0

TY @Used_By_Already。今天我會玩你的建議。另外我想再添加一個常量到我的where子句:日期範圍。在where子句中總會有一個數據>和日期<。如果這改變了你的建議,請讓我知道。 – mwex501

+0

我嘗試了多次複合索引變體的迭代,包括您的具體建議,並且都增加了搜索時間。在cat/subcat/family/sku AND之前,我確實發現調整PRIMARY KEY優先考慮日期類型/日期列,包括所選列在內有重大影響。此主鍵將搜索時間縮短爲0.8秒:添加主鍵(propId,elemType,dateType,日期,類別,子類別,系列,sku,商品銷售額,grossSales,數量,訂單,運費,稅收,詳細信息)。仍在尋求改進 - 感謝您的幫助! – mwex501

+0

這是一個好消息,對我來說,你已經掌握了使用和利用解釋輸出的技巧。做得好。 –