1. 程式人生 > >MySql資料庫分表分割槽實踐

MySql資料庫分表分割槽實踐

1. 背景 —— 公司物聯網專案

海量裝置通過物聯網服務接入雲端,裝置每30s上報一次自身資料(以下稱為動態資料)。 物聯網服務將裝置上報的資料轉發給資料處理閘道器,由資料入庫閘道器執行批量入庫操作插入資料庫。 專案大致技術架構如下圖:

2. 問題

接入的裝置數量較大時,上報的動態資料資料量過大,導致單表查詢過慢。

假設有1萬臺裝置,每臺裝置每30秒上報一次動態資料,那每分鐘就會產生2萬條資料,每天會產生2880萬條資料,一年將會產生100億條以上的資料。

這麼大的資料量如果進行單表查詢資料庫分析等操作延遲是完全無法接受的,故需要尋找一種解決方案。

3. 技術背景

3.1 分表

這裡的分表指的是根據裝置的序列號將一定數量的裝置拆分儲存在不同的表中,減少單表的資料量級。

3.2 分割槽

MySql資料庫中的資料是以檔案的形勢存在磁碟上的,預設放在/mysql/data下面(可以通過my.cnf中的datadir來檢視)。

一張表主要對應著三個檔案,一個是frm存放表結構,一個是myd存放表資料,一個是myi存表索引。如果一張表的資料量太大,mydmyi就會變的很大,查詢資料就會變的很慢。

MySql的分割槽功能,在物理上將這一張表對應的三個檔案,分割成許多個小塊,這樣查詢一條資料時,就不用全部查找了,只要知道這條資料在哪一塊,然後在那一塊找就行了,這樣就可以很大的提高資料查詢的效率。

MySql5.1及以上版本支援分割槽功能。 MySql的分割槽方法主要有:

3.2.1 range分割槽:

range分割槽意思就是以某個欄位為基準的連續分割槽,比如id小於3的1個分割槽,id小於6的一個分割槽,id小於100的一個分割槽。

3.2.2 list分割槽:

list分割槽就是以某個欄位為基準,該欄位從屬於一個列表範圍內的分割槽,比如id為1,3,5,7的一個分割槽,2,4,6,8的一個分割槽。

3.2.3 hash分割槽:

hash分割槽用於確保資料在預先設定數目的分割槽中平均分佈,比如預先設定分割槽數量為3個,則所有資料都會被平均分佈在3個分割槽內。

3.2.4 key分割槽:

按照KEY進行分割槽類似於按照HASH分割槽,除了HASH分割槽使用的使用者定義的表示式,而KEY分割槽的 雜湊函式是由MySQL 伺服器提供。

3.2.5 子分割槽:

子分割槽是分割槽表中每個分割槽的再次分割,子分割槽既可以使用HASH希分割槽,也可以使用KEY分割槽。這也被稱為複合分割槽(composite partitioning)。 子分割槽需遵循以下規則:

  • 如果一個分割槽中建立了子分割槽,其他分割槽也要有子分割槽
  • 如果建立了了分割槽,每個分割槽中的子分割槽數必有相同
  • 同一分割槽內的子分割槽,名字不相同,不同分割槽內的子分割槽名子可以相同(5.1.50不適用)

4. 解決方案

4.1 分表設計

設計為每1000個裝置一張表,表名為t_data_序號。

假設有1萬臺裝置,則根據裝置序列號將資料分散儲存在t_data_1 ~ t_data_10 十張表中。

同時增加一張裝置-動態資料關係表(表名t_device_table_map)來儲存裝置和動態資料表的關係,以便對裝置資料做增刪改查操作時能找到它對應的表,t_device_table_map表的結構如下:

應用平臺匯入裝置時,根據裝置數量判斷匯入裝置的動態資料應該儲存在哪張表,並將裝置和動態資料表關係寫入到t_device_table_map中。

資料處理啟動時載入t_device_table_map表資料到自己的記憶體中,然後在將裝置上報的資料入庫前從自身記憶體讀取該裝置屬於哪個動態資料表,再組裝Sql執行入庫操作。

4.2 分割槽設計

由於裝置的資料是持續性上報的,所以考慮使用Range分割槽。

分割槽設計為以資料採集時間為基礎,每週一個分割槽,每張表預設10年的分割槽。 按每個裝置每30秒上報一條資料計算,每個分割槽大約有 10002460*2 = 2880000條資料。

建表語句如下:

CREATE TABLE `t_data_1` (
  `i_id` bigint(20) NOT NULL AUTO_INCREMENT,
  `i_status` bit(1) DEFAULT NULL,
  `c_device_sequence` varchar(32) DEFAULT NULL COMMENT '裝置序列號',
  `t_collect_time` datetime NOT NULL DEFAULT '0000-00-00 00:00:00' COMMENT '資料採集時間',
  ...
  PRIMARY KEY (`i_id`,`t_collect_time`),
  KEY `index_c_device_sequence` (`c_device_sequence`,`t_collect_time`) USING BTREE
) ENGINE=MyISAM AUTO_INCREMENT=398404 DEFAULT CHARSET=utf8
/*!50500 PARTITION BY RANGE  COLUMNS(t_collect_time)
(PARTITION p20171224 VALUES LESS THAN ('2017-12-24 00:00:00') ENGINE = MyISAM,
 PARTITION p20171231 VALUES LESS THAN ('2017-12-31 00:00:00') ENGINE = MyISAM,
 PARTITION p20180107 VALUES LESS THAN ('2018-01-07 00:00:00') ENGINE = MyISAM,
 ...
 PARTITION p20271212 VALUES LESS THAN ('2027-12-12 00:00:00') ENGINE = MyISAM,
 PARTITION p20271219 VALUES LESS THAN ('2027-12-19 00:00:00') ENGINE = MyISAM) */;
/*!40101 SET character_set_client = @saved_cs_client */;

複製程式碼

5. 測試

以120萬條資料測試,分表(10張)分割槽查詢時間為0.1秒左右,見下圖:

不分表也不分割槽,查詢時間需要1秒以上,見下圖:

分表分割槽帶來的效能提升是很明顯的。

6. 思考

分割槽的數量是不是越多越好呢?肯定不是的。

因為MySQL在執行查詢操作的時候首先要去檢索查詢範圍在哪些分割槽內,分割槽太多,這部分的操作耗時就增加了。此外分割槽過多,可能會導致記憶體佔用升高的問題。

怎麼樣分割槽,分多少個區才最合適,還需要長期的觀察和大量資料的實驗。