簡體   English   中英

優化Mysql查詢左連接

[英]Optimization Mysql Query Left Join

我們希望通過以下查詢將calibration_data的條目映射到校准數據。 但是在我看來這個查詢的持續時間太長了(> 24h)。

有可能進行任何優化嗎? 我們現在添加了根據需要測試更多索引,但它對持續時間沒有任何影響。

[編輯]

硬件不應該是最大的瓶頸

  • 128 GB RAM
  • 1TB SSD RAID 5
  • 32核心

EXPLAIN結果

+----+-------------+-------+------------+------+---------------+------+---------+------+---------+----------+------------------------------------------------+
| id | select_type | table | partitions | type | possible_keys | key  | key_len | ref  | rows    | filtered | Extra                                          |
+----+-------------+-------+------------+------+---------------+------+---------+------+---------+----------+------------------------------------------------+
|  1 | SIMPLE      | cal   | NULL       | ALL  | NULL          | NULL | NULL    | NULL |    2009 |   100.00 | Using temporary; Using filesort                |
|  1 | SIMPLE      | m     | NULL       | ALL  | visit         | NULL | NULL    | NULL | 3082466 |   100.00 | Range checked for each record (index map: 0x1) |
+----+-------------+-------+------------+------+---------------+------+---------+------+---------+----------+------------------------------------------------+

查詢耗時太長:

Insert into knn_data (SELECT cal.X           AS X, 
        cal.Y           AS Y, 
        cal.BeginTime   AS BeginTime, 
        cal.EndTime     AS EndTime, 
        avg(m.dbm_ant)  AS avg_dbm_ant, 
        m.ant_id        AS ant_id, 
        avg(m.location) avg_location, 
        count(*)        AS count, 
        m.visit 
 FROM   calibration cal 
        LEFT join calibration_data m
          ON m.visit BETWEEN cal.BeginTime AND cal.EndTime 
 GROUP  BY cal.X, 
           cal.Y, 
           cal.BeginTime, 
           cal. BeaconId, 
           m.ant_id,
           m.macHash,
           m.visit; 

表knn_data:

    CREATE TABLE `knn_data` (
  `X` int(11) NOT NULL,
  `Y` int(11) NOT NULL,
  `BeginTime` datetime NOT NULL,
  `EndTIme` datetime NOT NULL,
  `avg_dbm_ant` float DEFAULT NULL,
  `ant_id` int(11) NOT NULL,
  `avg_location` float DEFAULT NULL,
  `count` int(11) DEFAULT NULL,
  `visit` datetime NOT NULL,
  PRIMARY KEY (`ant_id`,`visit`,`X`,`Y`,`BeginTime`,`EndTIme`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;

表校准

BeaconId, X, Y, BeginTime, EndTime
41791, 1698, 3944, 2016-11-12 22:44:00, 2016-11-12 22:49:00


CREATE TABLE `calibration` (
  `BeaconId` int(11) DEFAULT NULL,
  `X` int(11) DEFAULT NULL,
  `Y` int(11) DEFAULT NULL,
  `BeginTime` datetime DEFAULT NULL,
  `EndTime` datetime DEFAULT NULL,
  KEY `x,y` (`X`,`Y`),
  KEY `x` (`X`),
  KEY `y` (`Y`),
  KEY `BID` (`BeaconId`),
  KEY `beginTime` (`BeginTime`),
  KEY `x,y,beg,bid` (`X`,`Y`,`BeginTime`,`BeaconId`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;

表校准_數據

macHash, visit, dbm_ant, ant_id, mac, isRand, posX, posY, sources, ip, dayOfMonth, location, am, ar
'f5:dc:7d:73:2d:e9', '2016-11-12 22:44:00', '-87', '381', 'f5:dc:7d:73:2d:e9', NULL, NULL, NULL, NULL, NULL, '12', '18.077636300207715', 'inradius_41791', NULL


CREATE TABLE `calibration_data` (
  `macHash` varchar(100) COLLATE utf8_bin NOT NULL,
  `visit` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
  `dbm_ant` int(3) NOT NULL,
  `ant_id` int(11) NOT NULL,
  `mac` char(17) COLLATE utf8_bin DEFAULT NULL,
  `isRand` tinyint(4) DEFAULT NULL,
  `posX` double DEFAULT NULL,
  `posY` double DEFAULT NULL,
  `sources` int(2) DEFAULT NULL,
  `ip` int(10) unsigned DEFAULT NULL,
  `dayOfMonth` int(11) DEFAULT NULL,
  `location` varchar(80) COLLATE utf8_bin DEFAULT NULL,
  `am` varchar(300) COLLATE utf8_bin DEFAULT NULL,
  `ar` varchar(300) COLLATE utf8_bin DEFAULT NULL,
  KEY `visit` (`visit`),
  KEY `macHash` (`macHash`),
  KEY `ant, time` (`dbm_ant`,`visit`),
  KEY `beacon` (`am`),
  KEY `ant_id` (`ant_id`),
  KEY `ant,mH,visit` (`ant_id`,`macHash`,`visit`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin;

一次性任務? 那沒關系? 獲取此數據后,您是否會每天逐步更新“匯總表”?

收縮數據類型 - 龐大的數據需要更長的時間來處理。 示例:4字節的INT DayOfMonth可以是1字節的TINYINT UNSIGNED

您正在將TIMESTAMP移動到DATETIME 這可能會或可能不會如您所願。

對於IPv4, INT UNSIGNED可以正常使用,但您無法在其中使用IPv6。

COUNT(*)可能不需要4字節的INT ; 看到較小的變種。

在適當的地方使用UNSIGNED

mac-address以你擁有的方式占用19個字節; 它可以很容易地轉換為6字節的BINARY(6) 參見REPLACE()UNHEX()HEX()等。

innodb_buffer_pool_size的設置是innodb_buffer_pool_size 你擁有的大RAM可能大約100G。

時間范圍重疊嗎? 如果沒有,請利用這一點。 另外,不要在PRIMARY KEY包含不必要的列,例如EndTime

使GROUP BY列的順序與knn_data的PRIMARY KEY相同; 這樣可以避免在INSERT期間發生大量的塊分裂。

最大的問題是在calibration_data沒有有用的索引,因此JOIN必須一次又一次地進行全表掃描! 3M排的2K掃描結果! 讓我關注這個問題......

沒有好辦法做WHERE x BETWEEN start AND end因為MySQL不知道日期時間范圍是否重疊。 在這種情況下,沒有真正的解決辦法,所以讓我以不同的方式處理它......

開始和結束是“常規”嗎? 喜歡每個小時? 因此,我們可以做一些計算而不是 BETWEEN 如果是這種情況,請告訴我。 我會繼續思考。

對於“范圍”查詢來說,這是一個令人討厭且經典的問題:優化器不會使用您的索引並最終進行全表掃描。 在您的解釋計划中,您可以在列type=ALL上看到此信息。

理想情況下,您應該在鍵列中具有type=range和某些內容

一些想法:


我懷疑你改變你的聯合

ON m.visit BETWEEN cal.BeginTime AND cal.EndTime 

ON m.visit >= cal.BeginTime AND m.visit <= cal.EndTime

會工作,但仍然試一試。


在兩個表上觸發ANALYSE TABLE 這將更新表上的統計信息,可能有助於優化器做出正確的決策(即使用索引)


將查詢更改為此可能也有助於強制優化器使用索引:

Insert into knn_data (SELECT cal.X           AS X, 
        cal.Y           AS Y, 
        cal.BeginTime   AS BeginTime, 
        cal.EndTime     AS EndTime, 
        avg(m.dbm_ant)  AS avg_dbm_ant, 
        m.ant_id        AS ant_id, 
        avg(m.location) avg_location, 
        count(*)        AS count, 
        m.visit 
 FROM   calibration cal 
        LEFT join calibration_data m
          ON m.visit >= cal.BeginTime 
 WHERE m.visit <= cal.EndTime 
 GROUP  BY cal.X, 
           cal.Y, 
           cal.BeginTime, 
           cal. BeaconId, 
           m.ant_id,
           m.macHash,
           m.visit; 

這就是我所想的一切......

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM