2014-08-31 126 views
-3

我正在開發專業的基於Web的服務,能夠監控電能消耗或能源生產(即來自光伏或風能源)。該項目使用專有電子(由我開發)收集電氣參數,如電壓,電流和相角。多個JOIN的MySQL性能問題

  • 遠程設備將這些參數發送到Apache Web服務器腳本,該腳本將原始數據推送到託管在單獨服務器上的MySQL數據庫中。
  • 每個遠程設備都有自己的DEVICE_ID。
  • 數據每30秒發送一次,因此有一天我們每個設備都有2880行。

雖然MySQL服務器的計算能力很強大,但Apache服務器不顯示任何性能問題我無法在不到60秒的時間內執行查詢。我使用了所有的工具(鍵和索引)並正確設計了查詢(我希望),但我無法理解錯誤。我在DB設計方面的經驗主要來自Oracle和SQL Server,我在MySQL上的經驗非常有限(作爲專業人員)。

服務器硬件:在Windows Server 2008上運行的2x Xeon CPU 64位+ 4GB RAM,是的MySQL安裝在Windows2008上,因爲它是我非常熟悉的平臺。

該數據庫是非常簡單的:

表1:DATA_RAW由託管的電氣參數幾個字段,加上包含數據生,包含遠程設備ID字段DEVICE_ID的時間戳的字段SRV_TIMESTAMP。

所有遠程設備每30秒將數據推送到此表中。主鍵是一個集羣:DEVICE_ID + SRV_TIMESTAMP與這些字段一樣,不可能有來自同一設備的重複行。

該系統還接收氣象數據,如溫度,壓力,溼度,雲等。他們每小時發送一次。這些數據被推送到另一個名爲WEATHER_DATA的表中,主鍵又是一個集羣:DEVICE_ID + SRV_TIMESTAMP。唯一的區別是,我們每天在這裏每個設備只有24行。

,其中包含有關於太陽輻射對每個設備信息命名SUN_DATA第三個表。這用於計算PV場效率。託管這些數據的表名爲SUN_DATA,幷包含各種字段,主密鑰又是一個集羣:DEVICE_ID + SRV_TIMESTAMP。

重要的是要注意的是,SRV_TIMESTAMP在所有設備之間同步,以便任何數據集將共享相同的時隙(每天提供2880個時隙中的一個)是很重要的。

這裏從DATA_RAW表來的數據的一個示例:

SRV_TIMESTAMP  | DEVICE_ID | VOLTAGE | CURRENT | PHASE 
----------------------------------------------------------- 
2014-08-21 22:23:30 | 0AF500100 |  243 |  5.4 | 0.01 
2014-08-21 22:23:30 | 0AF456102 |  240 |  3.4 | 0.15 
2014-08-21 22:23:30 | 0BFDE0010 |  239 |  2.4 | 0.65 
2014-08-21 22:23:00 | 0AF500100 |  241 |  5.2 | 0.37 
2014-08-21 22:23:00 | 0AF456102 |  239 |  3.4 | 0.12 
2014-08-21 22:23:00 | 0BFDE0010 |  238 |  2.5 | 0.64 
2014-08-21 22:22:30 | 0AF500100 |  240 |  5.4 | 0.02 
2014-08-21 22:22:30 | 0AF456102 |  236 |  3.2 | 0.16 
2014-08-21 22:22:30 | 0BFDE0010 |  239 |  2.0 | 0.67 

這裏從DATA_SUN表來的數據的一個示例:

SRV_TIMESTAMP  | DEVICE_ID | SUNPOWER| SUNAZIMUTH 
------------------------------------------------------ 
2014-08-21 22:23:30 | 0AF500100 | 845674 |  175.1 
2014-08-21 22:23:30 | 0AF456102 | 866467 |  175.2 
2014-08-21 22:23:30 | 0BFDE0010 | 867686 |  175.4 
2014-08-21 22:23:00 | 0AF500100 | 867685 |  175.6 
2014-08-21 22:23:00 | 0AF456102 | 867876 |  175.9 
2014-08-21 22:23:00 | 0BFDE0010 | 867855 |  176.0 
2014-08-21 22:22:30 | 0AF500100 | 867879 |  176.2 
2014-08-21 22:22:30 | 0AF456102 | 856578 |  176.4 
2014-08-21 22:22:30 | 0BFDE0010 | 876789 |  176.4 

這裏來自的數據的樣本DATA_WEATHER表:

SRV_TIMESTAMP  | DEVICE_ID | CLOUDS | TEMPERATURE 
------------------------------------------------------ 
2014-08-21 22:00:00 | 0AF500100 |  30 |  36.1 
2014-08-21 22:00:00 | 0AF456102 |  35 |  26.2 
2014-08-21 22:00:00 | 0BFDE0010 |  34 |  35.4 
2014-08-21 21:00:00 | 0AF500100 |  70 |  36.6 
2014-08-21 21:00:00 | 0AF456102 |  10 |  26.9 
2014-08-21 21:00:00 | 0BFDE0010 |  20 |  35.0 
2014-08-21 20:00:00 | 0AF500100 |  30 |  32.2 
2014-08-21 20:00:00 | 0AF456102 |  20 |  23.4 
2014-08-21 20:00:00 | 0BFDE0010 |  65 |  34.4 

請注意,對於僅天氣,數據會在每個侯而對於其他表格數據每30秒推動一次。 這裏的DATA_RAW表(其他2臺都差不多,場只是名稱不同)詳細的表結構:

CREATE TABLE IF NOT EXISTS `data_raw` (
    `SRV_TIMESTAMP` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00', 
    `DEVICE_ID` char(5) NOT NULL, 
    `VOLTAGE` decimal(2,0) NOT NULL, 
    `CURRENT` decimal(2,0) NOT NULL, 
    `PHASE` decimal(3,0) NOT NULL 
) 
ENGINE=InnoDB 
DEFAULT CHARSET=utf8 
COMMENT='RAW DATA COMING FROM DEVICE IN A SINGLE TIMESLOT'; 

ALTER TABLE `data_raw` 
    ADD PRIMARY KEY (`DEVICE_ID`,`SRV_TIMESTAMP`) COMMENT 'PRIMARY KEY', 
    ADD KEY `IDX_DEVICE_ID` (`DEVICE_ID`); 

現在的問題:

我需要計算的各種數據,並以這樣做,我加入提供氣象數據,並與太陽數據電氣數據如下:

SELECT 
    D.VOLTAGE, 
    D.CURRENT, 
    S.SUNPOWER1, 
    S.SUNAZIMUTH, 
    W.CLOUDS, 
    W.TEMPERATURE 
FROM 
    DATA_RAW AS D 
    JOIN SUN_DATA AS S ON 
    S.SRV_TIMESTAMP=D.SRV_TIMESTAMP 
    AND S.DEVICE_ID=D.DEVICE_ID 
    LEFT JOIN WEATHER_DATA AS W ON 
    HOUR(W.SRV_TIMESTAMP)=HOUR(D.SRV_TIMESTAMP) 
    AND MONTH(W.SRV_TIMESTAMP)=MONTH(D.SRV_TIMESTAMP) 
    AND YEAR(W.SRV_TIMESTAMP)=YEAR(D.SRV_TIMESTAMP) 
    AND S.DEVICE_ID=D.DEVICE_ID 
ORDER BY D.SRV_TIMESTAMP DESC 

此查詢時間超過60秒,在DATA_RAW和SUN_DATA只是40.000行和WEATHER_DATA 150行。

將字段順序更改爲聯接沒有任何好處。 錯誤在哪裏?

+0

如果不知道表格和索引,很難確切知道出了什麼問題,因此您可能希望使問題更加簡潔並提供相關信息,但一個顯而易見的問題是您在過濾子句中使用函數。即使你有索引,當你使用'HOUR(W.SRV_TIMESTAMP)'之類的東西時,你也可以防止任何索引被使用,並且你將擁有一個全表掃描功能,並將該功能應用於每一行。 – 2014-08-31 19:29:35

+2

1.你的問題很難閱讀。請將其分成多個段落。 2.您已經描述了這些表格,但尚未指定您擁有的索引。 3.如果可能的話,提供一個帶有真實數據的數據庫轉儲(不需要是實際的數據,但類似的東西) – some 2014-08-31 19:30:16

+0

我已經遵循@some建議,希望現在的問題更容易閱讀。 – 2014-08-31 21:56:09

回答

1

我做了一些測試,並在我的硬件(英特爾至強CPU E3-1220(4核),16GB,運行Linux和MariaDb(mysql的替代品)上的查詢下降到不到0.2秒)

首先,我創建瞭如下表格。請注意,我增加了device_id中的字符數,並更改了十進制類型的精度和比例以匹配您提供的示例數據。我還爲data_raw添加了一個字段weatherts,該字段包含設備的最新天氣報告的時間戳。 (您可以在插入原始數據之前查詢最新的天氣預報時間戳,並且您還可以在獲取天氣預報時更新先前記錄的時間戳)。

CREATE TABLE IF NOT EXISTS `data_raw` (
    `SRV_TIMESTAMP` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00', 
    `DEVICE_ID` char(8) NOT NULL, 
    `VOLTAGE` decimal(3,0) NOT NULL, 
    `CURRENT` decimal(2,1) NOT NULL, 
    `PHASE` decimal(3,2) NOT NULL, 
    `weatherts` timestamp 
) 
ENGINE=InnoDB 
DEFAULT CHARSET=utf8 
COMMENT='RAW DATA COMING FROM DEVICE IN A SINGLE TIMESLOT'; 

CREATE TABLE IF NOT EXISTS `data_sun` (
    `SRV_TIMESTAMP` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00', 
    `DEVICE_ID` char(8) NOT NULL, 
    `SUNPOWER` decimal(10,0) NOT NULL, 
    `SUNAZIMUTH` decimal(4,1) NOT NULL 
) 
ENGINE=InnoDB 
DEFAULT CHARSET=utf8 
COMMENT='SUN DATA'; 


CREATE TABLE IF NOT EXISTS `data_weather` (
    `SRV_TIMESTAMP` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00', 
    `DEVICE_ID` char(8) NOT NULL, 
    `CLOUDS` decimal(2,0) NOT NULL, 
    `TEMPERATURE` decimal(3,1) NOT NULL 
) 
ENGINE=InnoDB 
DEFAULT CHARSET=utf8 
COMMENT='WEATHER DATA'; 

我創建的創建僞造的數據爲16個單位的腳本,每30秒data_raw和data_sun,並每隔一小時data_weather,導致46080行爲data_raw和data_sun和在data_weather 384行,每行的數據。

查詢data_rawSELECT * FROM data_raw大約需要0.10秒。

我嘗試了查詢的第一部分,在那裏我加入了data_raw和data_sun。如果沒有索引需要永遠的,所以我對data_sun創建索引:

CREATE UNIQUE INDEX SUN_PKEY ON data_sun (SRV_TIMESTAMP, DEVICE_ID); 

現在下面的查詢需要大約0.10秒了。

​​

爲了能夠做到完整的查詢我也data_weather添加索引:

CREATE UNIQUE INDEX WEATHER_PKEY ON data_weather (SRV_TIMESTAMP, DEVICE_ID); 

現在是時候來測試查詢:

SELECT 
    D.SRV_TIMESTAMP, D.DEVICE_ID, D.VOLTAGE, D.CURRENT, D.PHASE, 
    S.SUNPOWER, S.SUNAZIMUTH, 
    W.CLOUDS, W.TEMPERATURE 
FROM data_raw AS D 
    LEFT JOIN data_sun AS S ON 
    (S.SRV_TIMESTAMP=D.SRV_TIMESTAMP AND S.DEVICE_ID=D.DEVICE_ID) 
    LEFT JOIN data_weather AS W ON 
    (D.WEATHERTS = W.SRV_TIMESTAMP AND W.DEVICE_ID=D.DEVICE_ID) 
ORDER BY D.SRV_TIMESTAMP, D.DEVICE_ID; 

現在,我得到46080行0.13秒。由於讀取data_raw表花費了大約0.10秒,所以我認爲它非常好。

+0

太棒了,謝謝! – 2014-09-01 15:35:59

+0

太棒了,謝謝@some! 你給了我幾個很好的提示:放置FK鏈接天氣數據,避免在索引上使用函數是最好的想法,我不知道爲什麼我沒有這樣。只是因爲速度提高了很多。我正在改變表格和相關查詢的結構並測試表演。它看起來像問題的根源是HOUR()函數錯位在索引上,這導致MySQL不使用索引並掃描整個表。 反正一流的支持!謝謝! – 2014-09-01 15:43:03

+0

@PowerEngineering不錯,它爲你工作:)請接受這個答案。 – some 2014-09-01 17:35:12