MySql數據庫分表分區實踐

1. 背景 —— 公司物聯網項目

海量設備經過物聯網服務接入雲端,設備每30s上報一次自身數據(如下稱爲動態數據)。 物聯網服務將設備上報的數據轉發給數據處理網關,由數據入庫網關執行批量入庫操做插入數據庫。 項目大體技術架構以下圖:mysql

2. 問題

接入的設備數量較大時,上報的動態數據數據量過大,致使單表查詢過慢。sql

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

這麼大的數據量若是進行單表查詢數據庫分析等操做延遲是徹底沒法接受的,故須要尋找一種解決方案。bash

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的一個分區。spa

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在執行查詢操做的時候首先要去檢索查詢範圍在哪些分區內,分區太多,這部分的操做耗時就增長了。此外分區過多,可能會致使內存佔用升高的問題。

怎麼樣分區,分多少個區才最合適,還須要長期的觀察和大量數據的實驗。

相關文章
相關標籤/搜索