2017-02-27 26 views
1

我有一個超過34M行(並不斷增長)的MySQL數據庫表。在一個巨大的表上添加一個有效的索引

CREATE TABLE `sensordata` (
    `userID` varchar(45) DEFAULT NULL, 
    `instrumentID` varchar(10) DEFAULT NULL, 
    `utcDateTime` datetime DEFAULT NULL, 
    `dateTime` datetime DEFAULT NULL, 
    `data` varchar(200) DEFAULT NULL, 
    `dataState` varchar(45) NOT NULL DEFAULT 'Original', 
    `gps` varchar(45) DEFAULT NULL, 
    `location` varchar(45) DEFAULT NULL, 
    `speed` varchar(20) NOT NULL DEFAULT '0', 
    `unitID` varchar(5) NOT NULL DEFAULT '1', 
    `parameterID` varchar(5) NOT NULL DEFAULT '1', 
    `originalData` varchar(200) DEFAULT NULL, 
    `comments` varchar(45) DEFAULT NULL, 
    `channelHashcode` varchar(12) DEFAULT NULL, 
    `settingHashcode` varchar(12) DEFAULT NULL, 
    `status` varchar(7) DEFAULT 'Offline', 
    `id` int(11) NOT NULL AUTO_INCREMENT, 
    PRIMARY KEY (`id`), 
    UNIQUE KEY `id_UNIQUE` (`id`) 
) ENGINE=InnoDB AUTO_INCREMENT=98772 DEFAULT CHARSET=utf8 

我每分鐘從多個線程(至少400個線程)訪問此表以將數據插入表中。隨着表格的不斷增長,讀取和寫入數據的速度越來越慢。以前需要25秒左右的一個SELECT查詢,然後我加了一個唯一索引

UNIQUE INDEX idx_userInsDate (userID,instrumentID,utcDateTime)

這減少了讀取時間從25秒幾毫秒,但它增加了,因爲它必須更新索引的插入時間爲每個記錄。 另外如果我從多個線程運行SELECT查詢同時查詢花費太長的時間來返回數據。

這是一個例子查詢

Select dateTime from sensordata WHERE userID = 'someUserID' AND instrumentID = 'someInstrumentID' AND dateTime between 'startDate' AND 'endDate' order by dateTime asc; 

有人可以幫助我,以提高表架構或添加有效的指標,以提高性能,請。

預先感謝您

+0

UNIQUE(... datetime) - 紅旗!是否有變化,兩行將有效地具有相同的日期時間到第二個? –

+0

您是否嘗試修復建議的數據? – e4c5

+0

@ e4c5是的我正在處理它......數據太大我必須非常小心...... –

回答

1

一個PRIMARY KEY一個UNIQUE關鍵。折騰多餘的UNIQUE(id)

id是否被其他表引用?如果不是,那麼一起去掉它。相反,剛剛

PRIMARY KEY (userID, instrumentID, utcDateTime) 

也就是說,如果三重保證是唯一的。您提到了DST - 使用數據類型TIMESTAMP而不是DATETIME。這樣做,您可以根據需要轉換爲DATETIME,從而消除其中一列。

一個索引(PK)幾乎沒有空間,因爲它與InnoDB中的數據「聚集在一起」。

你的桌子非常胖,所有那些VARCHARs。例如,status可以縮減爲1個字節的ENUM。其他人可以正常化。像speed之類的東西可以是4字節的FLOAT或更小的DECIMAL,這取決於您需要多大的範圍和精度。

使用34M寬行,您可能最近超出了您擁有的RAM的可緩存性。通過縮小該行,您將推遲該溢出。

爲什麼攻擊索引?每個UNIQUE(包括PRIMARY)索引在允許插入行之前被檢查。通過將其降至1個指數,可以最大限度地降低成本。 (InnoDB真的需要一個PRIMARY KEY。)

INT是4個字節。你有十億個樂器嗎?也許instrumentID可能是SMALLINT UNSIGNED,這是2個字節,最大爲64K?想想所有其他的ID。

您有400 INSERTs /分鐘,正確嗎?這並不壞。如果你達到400 /秒,我們需要有一個不同的談話。

(「填充因子」在MySQL中是不可調,因爲它並沒有太大的差別。)

多少RAM你有? innodb_buffer_pool_size的設置是什麼?最佳值是可用的RAM的70%左右。

讓我們來看看您的主要查詢;可能還有其他問題需要解決。

+0

是的,在@ e4c5的回答後,我意識到表太胖了,必須重構... id沒有被任何其他表引用,將刪除它。 userID和instrumentID是字符串不是數字,所以不能改變它們,但我會改變速度,unitID,parameterID和其他 –

+0

使用ENUM將是完美的...我肯定會將數據狀態和狀態更改爲ENUM ... –

+0

在某些情況下,utcDateTime和dateTime與英國的工具相同,但在夏季DST會發生變化...我可以刪除dateTime列,但隨後我將不得不處理每條記錄,以便在可視化/處理數據時將UTC更改爲本地時區,這需要時間,因此我認爲我會保留它。 –

1

第一:避免索引,特別是ID的變種。 varchar中的每個字符位置在內部生成一個自己的索引條目!

第2種:您的選擇使用dateTime,您的索引設置爲utcDateTime。它只會取用戶ID和instrumentID,忽略utcDateTime-Part。

建議:改變你的數據類型的ID和改變你的指數匹配查詢(日期時間,不utcDateTime)

使用索引減少在插入你的表現,很不幸,沒有什麼,如填充因子對於mysql中的索引。所以你能做的最好的事情就是儘量使索引儘可能小。

重負載數據庫隨機訪問的另一種方法是:寫入未索引表,從索引表中讀取。在給定的時間,建立索引並交換表(可能需要第三個表創建索引,而其他表之間未觸及)。

+0

感謝您的建議,我添加了一個唯一的索引,以便同一個用戶+樂器+ utcdateTime沒有重複的記錄,可以有一些用戶+樂器+日期時間的重複記錄夏時制時間變化... –

+0

您正在您的表格上創建巨大的索引。這會減慢你的更新/刪除/插入大時間。最後,即使選擇操作將會很慢,除非你在SSD上,因爲隨機IO比順序IO慢得多 – e4c5

+0

是的,我應該選擇utcDateTime而不是dateTime ...我會改變查詢,但其他人已經指出正確,架構的表格應該改進 –

1

這不是錯誤的索引。這是你的數據類型。隨着磁盤上數據的增長,所有操作的速度都會降低。索引肯定有助於加快選擇 - 只要你的數據是正確的結構 - 但現在看來,它不是

CREATE TABLE `sensordata` (
    `userID` int, /* shouldn't this have a foreign key constraint? */ 
    `instrumentID` int, 
    `utcDateTime` datetime DEFAULT NULL, 
    `dateTime` datetime DEFAULT NULL, 
/* what exactly are you putting here? Are you sure it's not causing any reduncy? */ 
    `data` varchar(200) DEFAULT NULL, 
/* your states will be a finite number of elements. They can be represented by constants in your code or a set of values in a related table */ 
    `dataState` int, 
/* what's this? Sounds like what you are saving in location */ 
    `gps` varchar(45) DEFAULT NULL, 
    `location` point, 
    `speed` float, 
    `unitID` int DEFAULT '1', 
/* as above */ 
    `parameterID` int NOT NULL DEFAULT '1', 
/* are you sure this is different from data? */ 
    `originalData` varchar(200) DEFAULT NULL, 
    `comments` varchar(45) DEFAULT NULL, 
    `channelHashcode` varchar(12) DEFAULT NULL, 
    `settingHashcode` varchar(12) DEFAULT NULL, 
/* as above and isn't this the same as */ 
    `status` int, 
    `id` int(11) NOT NULL AUTO_INCREMENT, 
    PRIMARY KEY (`id`), 
    UNIQUE KEY `id_UNIQUE` (`id`) 
) ENGINE=InnoDB AUTO_INCREMENT=98772 DEFAULT CHARSET=utf8 
+0

實際上,它是索引「故障」:使用索引加速查找記錄,但由於索引必須重建而降低插入性能 – Psi

+0

@Psi這是正確的,這就是我在問題中陳述,你有建議,以改善索引,請 –

+0

我編輯我的答案相應 – Psi

相關問題