2014-10-28 70 views
4

我想問一個有關如何在使用InnoDB引擎大MySQL表提高性能的問題:提高性能的一大MySQL表

中目前我的數據庫有大約200萬行的表。該表格定期存儲由不同傳感器收集的數據。該表的結構如下:

CREATE TABLE sns_value (
    value_id int(11) NOT NULL AUTO_INCREMENT, 
    sensor_id int(11) NOT NULL, 
    type_id int(11) NOT NULL, 
    date timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, 
    value int(11) NOT NULL, 
    PRIMARY KEY (value_id), 
    KEY idx_sensor id (sensor_id), 
    KEY idx_date (date), 
    KEY idx_type_id (type_id)); 

起初,我還以爲在幾個月分區表的,但由於穩定增加新的傳感器,將在一個月左右達到目前的規模。

我想出的另一個解決方案是通過傳感器對錶格進行分區。但是,由於MySQL的1024個分區的限制,這不是一個選項。

我認爲,正確的解決辦法是使用具有相同結構的表中的每個傳感器:

sns_value_XXXXX

這樣將有超過1000臺3000萬的估計大小每年行數。同時,這些表格可以在幾個月內進行分區,以便最快速地訪問數據。

該解決方案會產生哪些問題?是否有更規範的解決方案?

編輯附加信息

我認爲表是關於大到我的服務器:

  • 雲2xCPU和8GB內存
  • LAMP(CentOS的6.5和MySQL 73年5月1日)

每個傳感器可能有多個變量類型(CO,CO2等)。

我主要有兩個慢查詢:

1)爲每個傳感器和類型(平均,最大值,最小值)每日摘要:

SELECT round(avg(value)) as mean, min(value) as min, max(value) as max, type_id 
FROM sns_value 
WHERE sensor_id=1 AND date BETWEEN '2014-10-29 00:00:00' AND '2014-10-29 12:00:00' 
GROUP BY type_id limit 2000; 

這需要超過5分鐘以上。

2)垂直到水平視圖和出口:

SELECT sns_value.date AS date, 
sum((sns_value.value * (1 - abs(sign((sns_value.type_id - 101)))))) AS one, 
sum((sns_value.value * (1 - abs(sign((sns_value.type_id - 141)))))) AS two, 
sum((sns_value.value * (1 - abs(sign((sns_value.type_id - 151)))))) AS three 
FROM sns_value 
WHERE sns_value.sensor_id=1 AND sns_value.date BETWEEN '2014-10-28 12:28:29' AND '2014-10-29  12:28:29' 
GROUP BY sns_value.sensor_id,sns_value.date LIMIT 4500; 

這也需要超過5分鐘。

其他考慮

  1. 時間戳可能由於插入件的特性進行重複。
  2. 定期插入必須與選擇共存。
  3. 沒有更新或刪除在表上執行。

的假定的製造「一個表中的每個傳感器」的方法

  1. 錶針對每個傳感器會小得多,以便訪問會更快。
  2. 每個傳感器只能在一張桌子上進行選擇。
  3. 選擇來自不同傳感器的混合數據對時間要求不高。

更新2015年2月2日

我們已經創建了一個新表,每年的數據,這是我們也每天劃分爲。每張桌子大約有2.5億行,有365個分區。使用的新索引與Ollie建議的(sensor_id,date,type_id,value)一樣,但查詢仍然需要30秒到2分鐘。我們不使用第一個查詢(每日摘要),僅使用第二個查詢(垂直到水平視圖)。

爲了能夠對錶格進行分區,必須刪除主索引。

我們錯過了什麼嗎?有沒有辦法提高性能?

非常感謝!

+0

當前結構發生了什麼問題? – 2014-10-28 17:57:08

+0

大?這裏很大嗎? – TomTom 2014-10-28 18:16:12

+0

teis數據的用途是什麼?你應該怎麼讀? – Aret 2014-10-28 19:28:18

回答

0

爲一系列傳感器創建單獨的表格將是一個想法。

如果不需要,請不要將auto_increment用於主鍵。通常數據庫引擎的主鍵是clustering the data

改爲使用組合鍵,取決於您的用例,列的順序可能不同。

編輯:也添加到PK的類型。考慮到這些問題,我會這樣做。選擇字段名稱是故意的,它們應該是描述性的,並始終考慮保留字。

CREATE TABLE snsXX_readings (
    sensor_id int(11) NOT NULL, 
    reading int(11) NOT NULL, 
    reading_time timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, 
    type_id int(11) NOT NULL, 

    PRIMARY KEY (reading_time, sensor_id, type_id), 
    KEY idx date_idx (date), 
    KEY idx type_id (type_id) 
); 

此外,請考慮彙總讀數或將它們分組爲單個字段。

+1

關於避免表分離和對主鍵建議完全不正確,您是正確的。如果有的話,對於MySQL和InnoDB,PK應該始終是auto_increment,並且幾乎不會複合或複合。原因在於你提到的非常集羣,以及InnoDB使用PK進行集羣的方式。 – 2014-10-28 20:58:28

+0

感謝您糾正我關於主鍵的問題,您可以參考一些關於InnoDB和PK用法的材料嗎? – Aret 2014-10-28 21:48:27

+1

Percona有許多有趣的文章。我不能給你幾個包含所有內容的鏈接,但[This pdf](http://www.percona.com/files/presentations/percona-live/london-2011/PLUK2011-b-tree- index-and-innodb.pdf)應該在這個問題上提出一些亮點。就我個人而言,我一直在閱讀文章並涉獵InnoDB源代碼以掌握它的功能。簡單地說,下一個PK值應該總是較大,以避免B樹重新平衡。基本上,auto_increment這樣做,所以它幾乎總是理想的候選人。 – 2014-10-28 22:00:58

1

編輯根據變化的問題每個傳感器

一個表,相對於,一個非常糟糕的主意確實如此。有幾個原因:在普通的操作系統

  1. 的MySQL服務器已經很難與萬的表。大多數操作系統無法同時處理多個同時進行的文件訪問。
  2. 每次添加(或刪除)傳感器時都必須創建表格。
  3. 涉及來自多個傳感器的數據的查詢將變得緩慢且令人費解。

我以前版本的答案建議按時間戳分區。但這不適用於您的value_id主鍵。但是,通過查詢以及適當的表格索引,分區可能不是必需的。

(如果你能避免列名date:這是一個保留字,你就會有很多麻煩編寫查詢的相反,我建議你使用ts,這意味着時間戳。)

謹防int(11)值對於您的value_id列來說不夠大。你將用盡IDS。對該列使用bigint(20)

你已經提到了兩個查詢。即使您將所有值保存在單個表中,使用適當的複合索引也可以使這兩個查詢非常高效。這是第一個。

SELECT round(avg(value)) as mean, min(value) as min, max(value) as max, 
     type_id 
    FROM sns_value 
WHERE sensor_id=1 
    AND date BETWEEN '2014-10-29 00:00:00' AND '2014-10-29 12:00:00' 
GROUP BY type_id limit 2000; 

對於此查詢,你第一次查找sensor_id使用恆定的,那麼你正在尋找一個範圍date值,那麼你被type_id聚集。最後你提取value列。因此,(sensor_id, date, type_id, value)上的所謂compound covering index將能夠通過索引掃描直接滿足您的查詢。這對你來說應該是非常快的 - 即使有大桌子,速度肯定也要快5分鐘。

在你的第二個查詢中,類似的索引策略將起作用。

SELECT sns_value.date AS date, 
     sum((sns_value.value * (1 - abs(sign((sns_value.type_id - 101)))))) AS one, 
     sum((sns_value.value * (1 - abs(sign((sns_value.type_id - 141)))))) AS two, 
     sum((sns_value.value * (1 - abs(sign((sns_value.type_id - 151)))))) AS three 
    FROM sns_value 
WHERE sns_value.sensor_id=1 
    AND sns_value.date BETWEEN '2014-10-28 12:28:29' AND '2014-10-29 12:28:29' 
GROUP BY sns_value.sensor_id,sns_value.date 
LIMIT 4500; 

再次,你開始的sensor_id恆定值,然後使用一個date範圍。您然後提取type_idvalue。這意味着我提到的相同的四列索引應該爲你工作。

CREATE TABLE sns_value (
    value_id bigint(20) NOT NULL AUTO_INCREMENT, 
    sensor_id int(11) NOT NULL, 
    type_id int(11) NOT NULL, 
    ts  timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, 
    value int(11) NOT NULL, 
    PRIMARY KEY  (value_id), 
    INDEX query_opt (sensor_id, ts, type_id, value) 
); 
+0

@DRapp行交換索引的原因與MySQL如何使用複合索引來滿足查詢有關。當我建議這兩個索引時,最初的提問者還沒有披露正在使用的查詢。 – 2014-10-29 17:08:01

+0

瞭解,謝謝 – DRapp 2014-10-29 17:28:55

+0

爲什麼要將tehe值添加到索引中? – Aret 2014-10-30 05:39:13

0

你可以嘗試得到隨機的彙總數據

我有類似的表。表引擎myisam(最小的表大小),10米記錄,因爲無用(測試)我的桌子上沒有索引。獲取所有數據的所有範圍。結果:10sn這個查詢。

SELECT * FROM (
     SELECT sensor_id, value, date 
     FROM sns_value l 
     WHERE l.sensor_id= 123 AND 
     (l.date BETWEEN '2013-10-29 12:28:29' AND '2015-10-29 12:28:29') 
     ORDER BY RAND() LIMIT 2000 
    ) as tmp 
    ORDER BY tmp.date; 

這個查詢在第一步獲取日期和排序隨機化前2k數據,在第二步排序數據。每次查詢得到2k結果爲不同的數據。