淺談mysql分表

關於分表:顧名思義就是一張數據量很大的表拆分成幾個表分別進行存儲。

我們先來大概瞭解以下一個數據庫執行SQL的過程:

接收到SQL --> 放入SQL執行隊列 --> 使用分析器分解SQL --> 按照分析結果進行數據的提取或者修改 --> 返回處理結果。在這個過程中一般比較花時間的是在隊列裏的等待時間和執行時間。歸根到底就是執行時間,執行時間減少了等待時間自然就變短了。

爲了保證數據的完整性,數據庫有鎖定機制。MySQL中有表鎖定和行鎖定,MySQL中myisam存儲引擎是表鎖定,innodb存儲引擎是行鎖定。分爲包含共享鎖和獨佔鎖兩種。獨佔鎖就是整個數據文件歸一個線程所有,其他線程就必須等待。如果數據太多,一次執行的時間太長,特別是在鎖表的情況下,就會導致大量的其他SQL等待執行,嚴重影響系統的正常使用。

另外更新表數據時會導致索引更新,當單表數據量很大時這個過程比較耗時,這就是爲什麼對大表進行新增操作會比較慢的原因。並且更新表數據會進行表級鎖或者行鎖,這樣就導致其他操作等待。

所以我們將大表拆分爲多個字表,那麼在更新或者查詢數據的時候,壓力會分散到不同的表上。由於分表之後每個表的數據較小,不管是查詢還是更新都極大的提高了速度,即使出現最壞的“鎖表”的情況,那其他表還是可以並行使用。

分表的幾種常見策略:

(1)預先估計某個大表的數據量,按實際情況將其均分爲固定數量表

根據分表算法,將數據平均分散到不同的數據表中,常見處理方式有對自增id取模、對某個字段進行hash。比如某系統用戶預計支持1億用戶數,分100個表存儲用戶數據,按照自增id的最後2位來分表,對100取模,那麼用戶數據表就是user_01~user_99。

(2)按時間拆分

對於那種根據時間增長較快的數據可以按時間拆分,根據業務實際情況按天、按月、按年等進行拆分。比如進銷存數據,我們可以按月分表,形如jxc_data_201201、jxc_data_201202

(3)按每個表固定記錄行數拆分

一般根據自增長ID拆表,每張表存儲指定數量的數據。一張表的數據行數到了指定數量,就自動保存到新的表裏。

(4)將很久之前的數據遷移到一張歷史表

比如日誌記錄,一般只會查詢3個月之內的日誌,對於超過三個月的日誌記錄我們可以遷移到到遷移到另一張表中,比如log_history

(5)分表之後的處理

前面已經說過分表的好處,但哪有那麼十全十美的事情呢,這個也不例外。分表之後,麻煩的事情來了,業務數據分散在各個分表中, 之前的業務功能如何保證呢?比如說我要插入一條記錄、更新一條記錄、刪除一條記錄、查詢統計數據,現在要怎麼處理呢。

以用戶信息維護功能爲例,假設我們有一張表user保存所有的用戶信息,拆分到100張表裏。我們按照用戶表自增id兩位尾數取模分表,那麼我們的用戶表應該是user_01~user_99。

先看新增操作,這時記錄還不存在,首先需要獲取用戶的唯一id,我們可以新建一張表來生成用戶的唯一id

CREATE TABLE `seq_user_id` (  

    `id` BIGINT( 20  ) NOT NULL AUTO_INCREMENT PRIMARY KEY  

) ENGINE = MYISAM;

那麼要新增用戶的時候,先插入一條記錄到seq_user_id拿到用戶id

有了用戶id之後,我們需要知道這個用戶數據要插入到哪一張用戶表裏去。

根據我們的拆表規則計算出實際存儲的表名,我們可以寫這樣一個函數來實現

function get_table_name($id) {  

    return 'user_'.intval($id)%100;  

}

這個時候我們就可以進行INSERT操作了。

更新和刪除用戶信息操作類似了,只是少了生成id的步驟,我們直接根據已有用戶的id獲取表名再來執行相應的UPDATE和DELETE動作。

如果我們要查詢某個用戶的信息,使用get_table_name獲取實際存儲該用戶信息的表,然後進行SELECT操作即可完成。

從這個例子看出,我們只要更具分表規則獲取到實際的數據表即可,其他與改造之前並沒有太多的不一樣,好像挺方便的。

但麻煩的事情來了,現在有一個需求要統計2011年12月到2012年3月這段時間註冊的用戶數,即需要根據時間段來查詢用戶信息。

想一下,用戶信息現在是分散在不同的數據表裏了,要怎麼做?

聯合這麼多表一起查詢麼?想想都覺得恐怖;

又或者是分別到這100張表裏分詢這個時間段的用戶數,然後再累加麼?好像也很麻煩

如何你用的user分表的存儲引擎是MyISAM,那麼恭喜你,這裏有一種很簡單的處理方法。

利用merge存儲引擎將拆分的表合併成一張表

 MERGE存儲引擎可以將N個子表聯合在一起,看成是一個整表,實際上還是N個真實的子表。

在我們查詢這個merge表就相當於查詢所有字表的數據了,非常方便。當然merge表還是有一定的限制的,具體請查看mysql官方手冊。

這裏引用MYSQL參考手冊中的片段來說明如何操作

下面例子說明如何創建一個MERGE表:

mysql> CREATE TABLE t1 (

    ->    a INT NOT NULL AUTO_INCREMENT PRIMARY KEY,

    ->    message CHAR(20));

mysql> CREATE TABLE t2 (

    ->    a INT NOT NULL AUTO_INCREMENT PRIMARY KEY,

    ->    message CHAR(20));

mysql> INSERT INTO t1 (message) VALUES ('Testing'),('table'),('t1');

mysql> INSERT INTO t2 (message) VALUES ('Testing'),('table'),('t2');

mysql> CREATE TABLE total (

    ->    a INT NOT NULL AUTO_INCREMENT,

    ->    message CHAR(20), INDEX(a))

    ->    TYPE=MERGE UNION=(t1,t2) INSERT_METHOD=LAST;

注意,一個列在MERGEN表中被索引,但沒有被宣告爲一個PRIMARY KEY,因爲它是在更重要的MyISAM表中。這是必要的,因爲MERGE表在更重要的表中的設置上強制非唯一性。

創建MERGE表之後,你可以發出把一組表當作一體來操作的查詢:

mysql> SELECT * FROM total;

+---+---------+

| a | message |

+---+---------+

| 1 | Testing |

| 2 | table   |

| 3 | t1      |

| 1 | Testing |

| 2 | table   |

| 3 | t2      |

+---+---------+

根據這個方法的介紹,我們可以創建一張user的合併表,然後對這張表進行查詢即可達到查詢分表所有的數據的效果。

當然如果表存儲引擎不是MyISAM的話就不能使用這個方法了,分別查詢之後合併查詢結果。

這裏有幾點個人建議供參考:

根據業務需求,選擇合適的分表方式,儘量減少需要一次查詢幾個分表的操作

分別對不同的分表進行操作,最後在應用層合併數據。網上有介紹使用多線程分別查詢各部分的數據最後合併的,效果應該不錯,操作起來有麻煩。

對一些實時性要求不高的數據使用緩存

實例:一步一步實現大表數據分表處理

假設現在系統裏有一個進銷存日結的數據表,目前一般只需要查詢某個月的數據,不用跨月查詢,那麼這裏我們就按月來分表。如果實際業務還是很多地方需要對多張拆分出來的表進行合併查詢的,就要重新考慮一下分表的方式了,可以按其他時間段或其他字段來拆分表。

這樣每個月的開始都要進行一個操作建立一張以月爲單位的新表來存儲過去一個月的數據。即每月都要建立一張表比如 jxc_2011_04,jxc_2011_05,jxc_2011_06……

這裏有一個小處理,有一張表的表明始終不變,用來保存當前月的數據,這樣做的好處是我們的應用程序保存進銷存數據時始終是操作jxc這種表,應用的默認查詢當月進銷存的功能也不用改動,始終是查詢的當前月的數據。當然需要查詢非當前月的數據時就需要通過時間進行定位實際存儲數據的表,當然如果無法避免跨月查詢,這個時候就需要應用程序進行合併處理了。

現在來看看實際如何操作,假如我們每天凌晨3點生成前一天的數據到jxc表,比如到2012-07-01的時候,就會進行執行如下操作:

create table struct_jxc like jxc;     -- 

rename table jxc to jxc_2012_06, struct_jxc to jxc;

這樣就將jxc表的數據即6月的數據全部遷移到jxc_2012_06表中,jxc現在就是空表,那麼從7月1號開始數據都是保存到jxc表中,依次8月1號的時候再進行類似的操作,即可實現了按月分表。

這裏用rename的好處就是不需要在進行轉移大量數據的時候進行導出和導入的操作,速度會快很多。

寫在後面:

關於mysql分表也在學習研究之中,一些問題也沒有完善的處理方案,如果你有什麼的建議可以告訴我。留下幾個問題,希望能起到拋磚引玉的效果。

1、你的業務數據更適合什麼分庫策略?

2、隨着系統使用年限的增加,如果發現最初分配的100張表還是難堪重負,要怎麼辦?

3、由於數據分佈在不同的表中,如何高效的進行負責的查詢分析?

4、如果拆分出來的表分佈在不同的服務器上,事務又該如何保證?

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章