2011-09-05 31 views
3

我看過這個問題幾乎在一些線程上回答,但沒有考慮到對這個特定域的影響:多個表或使用分區?

我期待在MySQL中存儲大量儀表的時間序列數據(500和增長),它們每隔5分鐘提供一次浮動值。在最簡單的,該結構將是: - gauge_id - 時間戳 - 值

(其中gauge_id和時間戳結合作爲主鍵)

這意味着每計大約每年105120行 - 所有這些都需要儲存10年或20年。對於1000臺儀表,我們每年會看1億條記錄。

數據是批量寫入的,每個通道的值通常會彙總到來自遠程源的XML文件中,並每小時或每天讀入數據庫。因此,最多隻能有一個小時的插入件數量與我們的量表一樣多。

基於時間範圍,數據的讀操作將按照量表進行(所以不需要在量表之間進行數據連接操作)。所以例如在兩個日期之間獲得量表X的所有值。 通常,這還包括某種形式的聚合/插值函數 - 因此用戶可能希望查看每日平均值或每週最大值等,以查看任意範圍。 同樣,讀取次數相對較少,但這些需要MySQL在1秒內做出響應。

在這個階段,我對每個量表的1個表進行了標準化,而不是在gauge_id上​​對MySQL中的一個巨大表進行分區。 這樣做的邏輯將使分片更容易,簡化備份,並且本質上使得如果在任何階段存在數據錯誤,則量表更容易移除/重建。 成本是寫入和讀取operatiosn都稍微複雜一點。

對此有何看法?

-------- -------- UPDATE

我跑我的MacBook 2.4GHz的Core 2 Duo處理器,RAM的4場音樂會一些測試。

設置如下表:

CREATE TABLE `test` (
    `channel_id` int(10) NOT NULL, 
    `time` datetime NOT NULL, 
    `value` int(10) NOT NULL, 
    KEY `channel_id` (`channel_id`) 
) ENGINE=MyISAM DEFAULT CHARSET=utf8; 

與存儲過程填充的:

CREATE PROCEDURE `addTestData`(IN ID INT, IN RECORDS INT) 
    BEGIN 
     DECLARE i INT DEFAULT 1; 
     DECLARE j DATETIME DEFAULT '1970-01-01 00:00:00'; 
      WHILE (i<=RECORDS) DO 
       INSERT INTO test VALUES(ID,j,999); 
       SET i=i+1; 
       SET j= j + INTERVAL 15 MINUTE; 
      END WHILE; 
    END $$ 

,而我則叫創建第一個100萬條記錄

call addTestData(1,1000000); 

插入在執行47秒

SELECT * FROM `test` WHERE channel_id = 1 and YEAR(time) = '1970'; 

在0.0006秒

SELECT AVG(value) as value, DATE(time) as date FROM `test` 
WHERE channel_id = 1 and YEAR(time) = '1970' group by date; 

在4.6秒(MAX,在同一時間執行SUM函數)執行的處理。

加入4個計後:

call addTestData(2,1000000); 
call addTestData(3,1000000); 
call addTestData(4,1000000); 
call addTestData(5,1000000); 

插入件在47秒執行的每個中,用於表

78兆字節我跑的相同的兩個查詢 - 並得到完全相同的執行時間與表中有100萬條記錄(更大的查詢爲4.6秒)。因此,禁止分片,備份和未來硬件驅動的變更對任何單個量表的表格(即多個讀數,數據間隔的變化)的潛在用途,似乎沒有必要爲可預見的分割成多個表格。甚至沒有嘗試用分區運行查詢,似乎沒有任何理由。

--------無論其-------------

由於4.6秒一個查詢是不理想的,我們顯然需要做一些優化。 作爲第一步,我重新調整了查詢​​:5萬條記錄(超過5 CHANNEL_ID的)的查詢需要4.3秒桌子上

SELECT 
    AVG(value) as value, 
    DATE(time) as date 
FROM 
    (SELECT * FROM test 
    WHERE channel_id = 1 and YEAR(time) = '1970') 
    as temp 
group by date; 

運行。 如果我在1通道,100萬條記錄的桌子上運行它,它會在0.36秒內運行! 抓我的頭有點過這...

分區的500萬條記錄

ALTER TABLE test PARTITION BY HASH(channel_id) PARTITIONS 5; 

表隨後完成了複合查詢以上0.35秒也同樣的性能增益。

回答

3

對於我來說,在您的場景中沒有任何理由通過標準進行分區,如果您在gauge_id上​​有索引,性能不會成爲問題,因爲MySql會立即使用索引找到與某個標準相關的行,之後其他操作就像處理每個量表的專用表一樣。

分區可能是合理的唯一情況是,如果您訪問非常新的規範數據(比如說最新的10%),那麼舊的數據(剩餘90%)如果這種情況分爲兩個「近期」 「歸檔」表格可能會給你很多性能優勢。

如果您對各個表的操作不涉及索引,那麼相同的操作不應該在合併的表上花費太多時間,因爲MySql首先使用gauge_id上​​的索引將結果縮小到某些量表行,如果操作涉及一個索引,您應該使索引成爲以'gauge_id'開頭的合併表的多列索引,例如INDEX(timestamp)在單個表格上應該變爲INDEX(gauge_id, timestamp),那麼在大多數情況下,操作將與單獨的表格相同。也不要被像'5億行'這樣的數字推遲,數據庫被設計成可以處理這些數據量。

我的評論大多是基於經驗,幾乎每次我遇到你的情況,並決定去個別表,由於某種原因我結束了合併表成爲一個,因爲大多數時候發生時該項目已經成熟,這是一個痛苦的過程。我真的經歷過「關係數據庫不是這樣使用的」。

我真的很喜歡聽到別人對此的評論,順便說一句,在做任何事情之前做很多測試,MySql有很多unexpected behaviors

+0

單個表(5億行)將如何執行: – Rean

+0

單個表(5億行)如何在一年內相隔兩年的兩個日期之間用選擇查詢在gauge_id上​​執行,AND要求結果成爲AVG一年中每個月的團體數量? (與僅有一個量表的500萬條記錄表上的同一查詢相比,可能劃分爲'最近'和'歸檔') – Rean

+0

只需添加 - 我也考慮管理5億行單個表的含義。我有預感,重建索引的任何東西都會很昂貴。或者例如一個批量更新查詢,其中一個量表被發現已經提供了6個月的誤校準值。 – Rean