在3个大表上使用内部联接优化SQL查询

时间:2017-11-21 20:16:06

标签: mysql sql query-optimization

我有一个SQL查询,其中3个表连接在远程MySQL数据库

其中两个表的大小约为15GB(STEP_RESULT和meas_numericlimit)

在发送数据之前,在服务器上创建一个TMP表,大约需要2.5小时才能结束

我不是服务器管理员,但我可以使用MySql WorkBench观察我的查询 此服务器是最新的64GB RAM

如何优化此查询?

谢谢

我的查询:

select 
t1.UUT_NAME, 
t1.STATION_NUM, 
t1.START_DATE_TIME, 
t3.LOW_LIMIT, 
t3.DATA,
t3.HIGH_LIMIT,
t3.UNITS,
t2b.STEP_NAME 
from 
meas_numericlimit t3 
    inner join STEP_RESULT t2a on t3.ID = t2a.STEP_ID
    inner join STEP_RESULT t2b on t2a.STEP_PARENT = t2b.STEP_ID
    inner join uut_result t1 on t2b.UUT_RESULT = t1.ID
where 
t1.UUT_NAME like 'Variable1-1%' and 
t1.STATION_NUM = 'variable2' and 
t2b.STEP_NAME = 'variable3' and 
t2b.STEP_TYPE = 'constant'

这里的SHOW TABLES和EXPLAIN输出查询:

+--------------------+
| Tables_in_spectrum |
+--------------------+
| cal_dates          |
| calibrage          |
| execution_time     |
| meas_numericlimit  |
| station_feature    |
| step_callexe       |
| step_graph         |
| step_msgjnl        |
| step_msgpopup      |
| step_passfail      |
| step_result        |
| step_seqcall       |
| step_stringvalue   |
| syst_event         |
| uptime             |
| users              |
| uut_result         |
+--------------------+

+----+-------------+-------+--------+-------------------------+--------

| id | select_type | table | type   | possible_keys           | key     
| 
+----+-------------+-------+--------+-------------------------+--------

|  1 | SIMPLE      | t2a   | ALL    | NULL                    | NULL    
|
|  1 | SIMPLE      | t3    | eq_ref | PRIMARY                 | PRIMARY 
|
|  1 | SIMPLE      | t2b   | ALL    | NULL                    | NULL    
|
|  1 | SIMPLE      | t1    | eq_ref | PRIMARY,FK_uut_result_1 | PRIMARY 
|
+----+-------------+-------+--------+-------------------------+--------


---------+----------------------+----------- +-------------------------

key_len  | ref                  |    rows    | Extra                     
|
---------+----------------------+----------- +-------------------------
NULL    | NULL                 | 48120004   |                           
|
40      | spectrum.t2a.STEP_ID |        1   |                           
|
 NULL    | NULL                 | 48120004   | Using where; Using join
                                                             buffer  |
40      | spectrum.t2b.UUT_RESULT |       1 | Using where               
|
-------+----------------------+------------+---------------------------

这里是SHOW CREATE TABLE:

CREATE TABLE `uut_result` (
`ID` varchar(38) NOT NULL DEFAULT '', 
`STATION_NUM` varchar(255) DEFAULT NULL, 
`SOFTVER_ODTGEN` varchar(10) DEFAULT NULL, 
`HARDVER_ODTGEN` varchar(10) DEFAULT NULL, 
`NEXT_CAL_DATE` date DEFAULT NULL, 
`UUT_NAME` varchar(255) DEFAULT NULL, 
`UUT_SERIAL_NUMBER` varchar(255) DEFAULT NULL, 
`UUT_VERSION` varchar(255) DEFAULT NULL, 
`USER_LOGIN_NAME` varchar(255) DEFAULT NULL, 
`USER_LOGIN_LOGIN` varchar(255) NOT NULL DEFAULT '', 
`START_DATE_TIME` datetime DEFAULT NULL, 
`EXECUTION_TIME` float DEFAULT NULL, 
`UUT_STATUS` varchar(255) DEFAULT NULL, 
`UUT_ERROR_CODE` int(11) DEFAULT NULL, 
`UUT_ERROR_MESSAGE` varchar(1023) DEFAULT NULL, 
`PAT_NAME` varchar(255) NOT NULL DEFAULT '', 
`PAT_VERSION` varchar(10) NOT NULL DEFAULT '', 
`TEST_LEVEL` varchar(50) DEFAULT NULL, 
`INTERFACE_ID` int(10) unsigned NOT NULL DEFAULT '0', 
`EXECUTION_MODE` varchar(45) DEFAULT NULL, 
`LOOP_MODE` varchar(45) DEFAULT NULL, 
`STOP_ON_FAIL` tinyint(4) unsigned NOT NULL DEFAULT '0', 
`EXECUTION_COMMENT` text, 
PRIMARY KEY (`ID`), 
KEY `FK_uut_result_1` (`STATION_NUM`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1;

CREATE TABLE `meas_numericlimit` (
`ID` varchar(38) NOT NULL DEFAULT '', 
`STEP_RESULT` varchar(38) NOT NULL DEFAULT '', 
`NAME` varchar(255) DEFAULT NULL, 
`COMP_OPERATOR` varchar(30) DEFAULT NULL, 
`HIGH_LIMIT` double DEFAULT NULL, 
`LOW_LIMIT` double DEFAULT NULL, 
`UNITS` varchar(255) DEFAULT NULL, 
`DATA` double DEFAULT NULL, 
`STATUS` varchar(255) DEFAULT NULL, 
`FORMAT` varchar(15) DEFAULT NULL, 
`NANDATA` int(11) DEFAULT '0', 
PRIMARY KEY (`ID`), 
KEY `FK_meas_numericlimit_1` (`STEP_RESULT`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1

CREATE TABLE `step_result` (
`ID` varchar(38) NOT NULL DEFAULT '', 
`UUT_RESULT` varchar(38) NOT NULL DEFAULT '', 
`STEP_PARENT` varchar(38) DEFAULT NULL, 
`STEP_NAME` varchar(255) DEFAULT NULL, 
`STEP_ID` varchar(38) NOT NULL DEFAULT '', 
`STEP_TYPE` varchar(255) DEFAULT NULL, 
`STATUS` varchar(255) DEFAULT NULL, 
`REPORT_TEXT` text, 
`DIAG` text, 
`ERROR_OCCURRED` tinyint(1) NOT NULL DEFAULT '0', 
`ERROR_CODE` int(11) DEFAULT NULL, 
`ERROR_MESSAGE` varchar(1023) DEFAULT NULL, 
`MODULE_TIME` float DEFAULT NULL, 
`TOTAL_TIME` float DEFAULT NULL, 
`NUM_LOOPS` int(11) DEFAULT NULL, 
`NUM_PASSED` int(11) DEFAULT NULL, 
`NUM_FAILED` int(11) DEFAULT NULL, 
`ENDING_LOOP_INDEX` int(11) DEFAULT NULL, 
`LOOP_INDEX` int(11) DEFAULT NULL, 
`INTERACTIVE_EXENUM` int(11) DEFAULT NULL, 
`STEP_GROUP` varchar(30) DEFAULT NULL, 
`STEP_INDEX` int(11) DEFAULT NULL, 
`ORDER_NUMBER` int(11) DEFAULT NULL, 
PRIMARY KEY (`ID`), 
KEY `FK_step_result_1` (`UUT_RESULT`), 
KEY `IDX_step_parent` (`STEP_PARENT`)
) ENGINE=MyISAM DEFAULT CHARSET=latin

2 个答案:

答案 0 :(得分:0)

如果这些是InnoDB表(而不是MyISAM),我会像这样编写查询:

SELECT t1.uut_name
     , t1.station_num
     , t1.start_date_time
     , t3.low_limit
     , t3.data
     , t3.high_limit
     , t3.units
     , t2b.step_name
  FROM uut_result t1
  JOIN step_result t2b
    ON t2b.uut_result     = t1.id
   AND t2b.step_type      = 'constant'
   AND t2b.step_name      = 'variable3' 
  JOIN step_result t2a
    ON t2a.step_parent    = t2b.step_id
  JOIN meas_numericlimit t3
    ON t3.id = t2a.step_id
 WHERE t1.station_num     = 'variable2'
   AND t1.uut_name     LIKE 'Variable1-1%'

我会创建合适的覆盖索引:

... uut_result_IX1 ON uut_result (station_num, uut_name, start_date_time, id)

... step_result_IX1 ON step_result (uut_result, step_type, step_name, step_id) 

... step_result_IX2 ON step_result (step_parent, step_id)

对于可能的性能提升,我还要考虑另外一个覆盖索引...

... meas_numericlimit_IX1 ON meas_numericlimit (id, low_limit, data, high_limit, units)

(对于InnoDB,主键列是群集键,因此这里没有什么好处。)

使用MyISAM,合适的索引很重要。但我并不认为覆盖索引会带来与InnoDB相同的好处。

使用MyISAM,覆盖索引不会避免访问基础表中的页面。所以我认为使用较短的索引会更好,只有谓词中使用的列:

... uut_result_IX1 ON uut_result (station_num, uut_name)

... step_result_IX1 ON step_result (uut_result, step_type, step_name)

... step_result_IX2 ON step_result (step_parent)

key_buffer_size增加到更大的值将允许缓存MyISAM索引;但不要过分配。除了OS文件系统缓存之外,MyISAM表页面没有缓存。

对MyISAM的其他配置参数进行修改(在我对MyISAM的有限经验中)具有仅仅过度分配(即浪费)内存的净效果,对性能的影响可忽略不计或微不足道。所以我不会惹这些。 (这并不是说有些天堂已经收获了一些性能提升。我的测试用例并没有取得任何成功。)

我没有动摇MyISAM调整,而是花费我的力气去游说这些表格改为InnoDB存储引擎。然后调整InnoDB。

<强> 修改

我建议的索引中列的顺序是基于具有相等比较的前导列...具有最多的选择&#34;选择性&#34;具有更多重复值的列之前的列。

这些索引是在考虑执行计划的情况下建议的......首先是t1(uut_result)作为驱动表,加入t2b,然后加入t2a ,最后加入t3

将谓词从WHERE子句移动到ON子句并不是为了获得性能......它是为了保持查询中每个表的谓词组合在一起,以帮助未来的读者。 / p>

我将查询视为对t1(uut_result)表的查询开始

SELECT t1.uut_name
     , t1.station_num
     , t1.start_date_time

--      , t3.low_limit
--      , t3.data
--      , t3.high_limit
--      , t3.units
--      , t2b.step_name

  FROM uut_result t1

--   JOIN step_result t2b
--     ON t2b.uut_result     = t1.id
--    AND t2b.step_type      = 'constant'
--    AND t2b.step_name      = 'variable3' 

--   JOIN step_result t2a
--     ON t2a.step_parent    = t2b.step_id

--   JOIN meas_numericlimit t3
--     ON t3.id = t2a.step_id

 WHERE t1.station_num     = 'variable2'
   AND t1.uut_name     LIKE 'Variable1-1%'

然后取消评论引用t2b ...

的行

答案 1 :(得分:0)

首先要注意的是,仅仅因为您在一个订单中编写联接并不意味着它们实际上按此顺序执行。 (查找声明性语言。)

出于这个原因,我首先要创建满足每个where子句的索引,然后是每个连接谓词,但是在两个方向上......

WHERE子句开始,所有复合/覆盖索引应以...开头

  • STEP_RESULT : (STEP_NAME, STEP_TYPE)
  • uut_result : (STATION_NUM, UUT_NAME)

同样考虑JOIN谓词,所有复合/覆盖索引应该是......

  • meas_numericlimit : (ID)
  • STEP_RESULT : (STEP_NAME, STEP_TYPE, STEP_ID)
  • STEP_RESULT : (STEP_NAME, STEP_TYPE, STEP_PARENT)
  • STEP_RESULT : (STEP_NAME, STEP_TYPE, UUT_RESULT)
  • uut_result : (STATION_NUM, UUT_NAME, ID)

在这5个索引中,您可能只会看到四个正在使用中,因此您可能希望删除未使用的索引,或者保留它以防统计信息更改更改说明计划。

这在某种程度上取决于数据的性质。例如,您可能只希望将uut_result上的索引“反转”为(ID, STATION_NUM, UUT_NAME)。如果不了解数据的行为,可能值得尝试。 (这同样适用于其他索引建议。)