總結下Mysql分表分庫的策略及應用

上月前面試某公司,對於mysql分表的思路,當時簡要的說了下hash算法分表,以及discuz分表的思路,
可是對於新增數據自增id存放的設計思想回答的不是很好(筆試+面試整個過程算是OK過了,因與我的預期的薪酬不太理想而忍痛放棄.),在此再深究下mysql 分表優化之類的設計思路方案.先來閒扯下發文目的:php

爲何要分表和分區?mysql

平常開發中咱們常常會遇到大表的狀況,所謂的大表是指存儲了百萬級乃至千萬級條記錄的表。這樣的表過於龐大,致使數據庫在查詢和插入的時候耗時太長,性能低下,若是涉及聯合查詢的狀況,性能會更加糟糕。分表和表分區的目的就是減小數據庫的負擔,提升數據庫的效率,一般點來說就是提升表的增刪改查效率。程序員

什麼是分表?面試

分表是將一個大表按照必定的規則分解成多張具備獨立存儲空間的實體表,咱們能夠稱爲子表,每一個表都對應三個文件,MYD數據文件,.MYI索引文件,.frm表結構文件。這些子表能夠分佈在同一塊磁盤上,也能夠在不一樣的機器上。app讀寫的時候根據事先定義好的規則獲得對應的子表名,而後去操做它。算法

什麼是分區?sql

分區和分表類似,都是按照規則分解表。不一樣在於分表將大表分解爲若干個獨立的實體表,而分區是將數據分段劃分在多個位置存放,能夠是同一塊磁盤也能夠在不一樣的機器。分區後,表面上仍是一張表,但數據散列到多個位置了。app讀寫的時候操做的仍是大表名字,db自動去組織分區的數據。數據庫

mysql分表和分區有什麼聯繫呢?
1.都能提升mysql的性高,在高併發狀態下都有一個良好的表現。
2.分表和分區不矛盾,能夠相互配合的,對於那些大訪問量,而且表數據比較多的表,咱們能夠採起分表和分區結合的方式(若是merge這種分表方式,不能和分區配合的話,能夠用其餘的分表試),訪問量不大,可是表數據不少的表,咱們能夠採起分區的方式等。
3.分表技術是比較麻煩的,須要手動去建立子表,app服務端讀寫時候須要計算子表名。採用merge好一些,但也要建立子表和配置子表間的union關係。
4.表分區相對於分表,操做方便,不須要建立子表。服務器

咱們知道對於大型的互聯網應用,數據庫單表的數據量可能達到千萬甚至上億級別,同時面臨這高併發的壓力。Master-Slave結構只能對數據庫的讀能力進行擴展,寫操做仍是集中在Master中,Master並不能無限制的掛接Slave庫,若是須要對數據庫的吞吐能力進行進一步的擴展,能夠考慮採用分庫分表的策略。併發

    1.分表app

       在分表以前,首先要選中合適的分表策略(以哪一個字典爲分表字段,須要將數據分爲多少張表),使數據可以均衡的分佈在多張表中,而且不影響正常的查詢。在企業級應用中,每每使用org_id(組織主鍵)作爲分表字段,在互聯網應用中每每是userid。在肯定分表策略後,當數據進行存儲及查詢時,須要肯定到哪張表裏去查找數據,

       數據存放的數據表 = 分表字段的內容 % 分表數量

     2.分庫

        分表可以解決單表數據量過大帶來的查詢效率降低的問題,可是不能給數據庫的併發訪問帶來質的提高,面對高併發的寫訪問,當Master沒法承擔高併發的寫入請求時,無論如何擴展Slave服務器,都沒有意義了。咱們經過對數據庫進行拆分,來提升數據庫的寫入能力,即所謂的分庫。分庫採用對關鍵字取模的方式,對數據庫進行路由。

     數據存放的數據庫=分庫字段的內容%數據庫的數量

    3.即分表又分庫
數據庫分表能夠解決單表海量數據的查詢性能問題,分庫能夠解決單臺數據庫的併發訪問壓力問題

當數據庫同時面臨海量數據存儲和高併發訪問的時候,須要同時採起分表和分庫策略。通常分表分庫策略以下:

中間變量 = 關鍵字%(數據庫數量*單庫數據表數量)

庫 = 取整(中間變量/單庫數據表數量)

表 = (中間變量%單庫數據表數量)

先談談分表的幾種方式:

一、mysql集羣

事實它並非分表,但起到了和分表相同的做用。集羣可分擔數據庫的操做次數,將任務分擔到多臺數據庫上。集羣能夠讀寫分離,減小讀寫壓力。從而提高數據庫性能。

二、自定義規則分表

大表能夠按照業務的規則來分解爲多個子表。一般爲如下幾種類型,也可本身定義規則。

1 Range(範圍)–這種模式容許將數據劃分不一樣範圍。例如能夠將一個表經過年份劃分紅若干個分區。
2 Hash(哈希)–這中模式容許經過對錶的一個或多個列的Hash Key進行計算,最後經過這個Hash碼不一樣數值對應的數據區域進行分區。例如能夠創建一個對錶主鍵進行分區的表。
3 Key(鍵值)-上面Hash模式的一種延伸,這裏的Hash Key是MySQL系統產生的。
4 List(預約義列表)–這種模式容許系統經過預約義的列表的值來對數據進行分割。
5 composite(複合模式) –以上模式的組合使用 

以聊天信息表爲例:

我事先建100個這樣的表,message_00,message_01,message_02……….message_98,message_99.而後根據用戶的ID來判斷這個用戶的聊天信息放到哪張表裏面,你能夠用hash的方式來得到,能夠用求餘的方式來得到,方法不少,各人想各人的吧。下面用hash的方法來得到表名:

<?php
function get_hash_table($table,$userid) {
 $str = crc32($userid);
 if($str<0){
  $hash = "0".substr(abs($str), 0, 1);
 }else{
  $hash = substr($str, 0, 2);
 }
 return $table."_".$hash;
}   
echo get_hash_table('message' , 'user18991');     //結果爲message_10
echo get_hash_table('message' , 'user34523');    //結果爲message_13
?> 

說明一下,上面的這個方法,告訴咱們user18991這個用戶的消息都記錄在message_10這張表裏,user34523這個用戶的消息都記錄在message_13這張表裏,讀取的時候,只要從各自的表中讀取就好了。

優勢:避免一張表出現幾百萬條數據,縮短了一條sql的執行時間

缺點:當一種規則肯定時,打破這條規則會很麻煩,上面的例子中我用的hash算法是crc32,若是我如今不想用這個算法了,改用md5後,會使同一個用戶的消息被存儲到不一樣的表中,這樣數據亂套了。擴展性不好。

3,利用merge存儲引擎來實現分表

我以爲這種方法比較適合,那些沒有事先考慮,而已經出現了得,數據查詢慢的狀況。這個時候若是要把已有的大數據量表分開比較痛苦,最痛苦的事就是改代碼,由於程序裏面的sql語句已經寫好了,如今一張表要分紅幾十張表,甚至上百張表,這樣sql語句是否是要重寫呢?舉個例子,我很喜歡舉例子

mysql>show engines;的時候你會發現mrg_myisam其實就是merge。

mysql> CREATE TABLE IF NOT EXISTS `user1` (
 ->   `id` int(11) NOT NULL AUTO_INCREMENT,
 ->   `name` varchar(50) DEFAULT NULL,
 ->   `sex` int(1) NOT NULL DEFAULT '0',
 ->   PRIMARY KEY (`id`)
 -> ) ENGINE=MyISAM  DEFAULT CHARSET=utf8 AUTO_INCREMENT=1 ;
Query OK, 0 rows affected (0.05 sec)   
mysql> CREATE TABLE IF NOT EXISTS `user2` (
 ->   `id` int(11) NOT NULL AUTO_INCREMENT,
 ->   `name` varchar(50) DEFAULT NULL,
 ->   `sex` int(1) NOT NULL DEFAULT '0',
 ->   PRIMARY KEY (`id`)
 -> ) ENGINE=MyISAM  DEFAULT CHARSET=utf8 AUTO_INCREMENT=1 ;
Query OK, 0 rows affected (0.01 sec)   
mysql> INSERT INTO `user1` (`name`, `sex`) VALUES('張映', 0);
Query OK, 1 row affected (0.00 sec)   
mysql> INSERT INTO `user2` (`name`, `sex`) VALUES('tank', 1);
Query OK, 1 row affected (0.00 sec)   
mysql> CREATE TABLE IF NOT EXISTS `alluser` (
 ->   `id` int(11) NOT NULL AUTO_INCREMENT,
 ->   `name` varchar(50) DEFAULT NULL,
 ->   `sex` int(1) NOT NULL DEFAULT '0',
 ->   INDEX(id)
 -> ) TYPE=MERGE UNION=(user1,user2) INSERT_METHOD=LAST AUTO_INCREMENT=1 ;
Query OK, 0 rows affected, 1 warning (0.00 sec)   
mysql> select id,name,sex from alluser;
+----+--------+-----+
| id | name   | sex |
+----+--------+-----+
|  1 | 張映    |   0 |
|  1 | tank   |   1 |
+----+--------+-----+
2 rows in set (0.00 sec)   
mysql> INSERT INTO `alluser` (`name`, `sex`) VALUES('tank2', 0);
Query OK, 1 row affected (0.00 sec)   
mysql> select id,name,sex from user2
 -> ;
+----+-------+-----+
| id | name  | sex |
+----+-------+-----+
|  1 | tank  |   1 |
|  2 | tank2 |   0 |
+----+-------+-----+
2 rows in set (0.00 sec) 
mysql> CREATE TABLE IF NOT EXISTS `user1` (  ->   `id` int(11) NOT NULL AUTO_INCREMENT,  ->   `name` varchar(50) DEFAULT NULL,  ->   `sex` int(1) NOT NULL DEFAULT '0',  ->   PRIMARY KEY (`id`)  -> ) ENGINE=MyISAM  DEFAULT CHARSET=utf8 AUTO_INCREMENT=1 ; Query OK, 0 rows affected (0.05 sec)  mysql> CREATE TABLE IF NOT EXISTS `user2` (  ->   `id` int(11) NOT NULL AUTO_INCREMENT,  ->   `name` varchar(50) DEFAULT NULL,  ->   `sex` int(1) NOT NULL DEFAULT '0',  ->   PRIMARY KEY (`id`)  -> ) ENGINE=MyISAM  DEFAULT CHARSET=utf8 AUTO_INCREMENT=1 ; Query OK, 0 rows affected (0.01 sec)  mysql> INSERT INTO `user1` (`name`, `sex`) VALUES('張映', 0); Query OK, 1 row affected (0.00 sec)  mysql> INSERT INTO `user2` (`name`, `sex`) VALUES('tank', 1); Query OK, 1 row affected (0.00 sec)  mysql> CREATE TABLE IF NOT EXISTS `alluser` (  ->   `id` int(11) NOT NULL AUTO_INCREMENT,  ->   `name` varchar(50) DEFAULT NULL,  ->   `sex` int(1) NOT NULL DEFAULT '0',  ->   INDEX(id)  -> ) TYPE=MERGE UNION=(user1,user2) INSERT_METHOD=LAST AUTO_INCREMENT=1 ; Query OK, 0 rows affected, 1 warning (0.00 sec)  mysql> select id,name,sex from alluser;
+----+--------+-----+
| id | name   | sex |
+----+--------+-----+
|  1 |  張映   |   0 |
|  1 | tank   |   1 |
+----+--------+-----+
2 rows in set (0.00 sec)
mysql> INSERT INTO `alluser` (`name`, `sex`) VALUES('tank2', 0); Query OK, 1 row affected (0.00 sec)  mysql> select id,name,sex from user2  -> ;
+----+-------+-----+
| id | name  | sex |
+----+-------+-----+
|  1 | tank  |   1 |
|  2 | tank2 |   0 |
+----+-------+-----+
2 rows in set (0.00 sec)

從上面的操做中,我不知道你有沒有發現點什麼?假如我有一張用戶表user,有50W條數據,如今要拆成二張表user1和user2,每張表25W條數據,

INSERT INTO user1(user1.id,user1.name,user1.sex)SELECT (user.id,user.name,user.sex)FROM user where user.id <= 250000
INSERT INTO user2(user2.id,user2.name,user2.sex)SELECT (user.id,user.name,user.sex)FROM user where user.id > 250000

這樣我就成功的將一張user表,分紅了二個表,這個時候有一個問題,代碼中的sql語句怎麼辦,之前是一張表,如今變成二張表了,代碼改動很大,這樣給程序員帶來了很大的工做量,有沒有好的辦法解決這一點呢?辦法是把之前的user表備份一下,而後刪除掉,上面的操做中我創建了一個alluser表,只把這個alluser表的表名改爲user就好了。可是,不是全部的mysql操做都能用的

 

a,若是你使用 alter table 來把 merge 表變爲其它表類型,到底層表的映射就被丟失了。取而代之的,來自底層 myisam 表的行被複制到已更換的表中,該表隨後被指定新類型。

b,網上看到一些說replace不起做用,我試了一下能夠起做用的。暈一個先

mysql> UPDATE alluser SET sex=REPLACE(sex, 0, 1) where id=2;
Query OK, 1 row affected (0.00 sec)
Rows matched: 1  Changed: 1  Warnings: 0   
mysql> select * from alluser;
+----+--------+-----+
| id | name   | sex |
+----+--------+-----+
|  1 | 張映    |   0 |
|  1 | tank   |   1 |
|  2 | tank2  |   1 |
+----+--------+-----+
3 rows in set (0.00 sec) 
mysql> UPDATE alluser SET sex=REPLACE(sex, 0, 1) where id=2; Query OK, 1 row affected (0.00 sec) Rows matched: 1  Changed: 1  Warnings: 0  mysql> select * from alluser;
 +----+--------+-----+
 | id | name   | sex |
 +----+--------+-----+
 |  1 | 張映    |   0 |
 |  1 | tank   |   1 |
 |  2 | tank2  |   1 |
 +----+--------+-----+
 3 rows in set (0.00 sec)

c,一個 merge 表不能在整個表上維持 unique 約束。當你執行一個 insert,數據進入第一個或者最後一個 myisam 表(取決於 insert_method 選項的值)。mysql 確保惟一鍵值在那個 myisam 表裏保持惟一,但不是跨集合裏全部的表。

d,當你建立一個 merge 表之時,沒有檢查去確保底層表的存在以及有相同的機構。當 merge 表被使用之時,mysql 檢查每一個被映射的表的記錄長度是否相等,但這並不十分可靠。若是你從不類似的 myisam 表建立一個 merge 表,你很是有可能撞見奇怪的問題。

c和d在網上看到的,沒有測試,你們試一下吧。

優勢:擴展性好,而且程序代碼改動的不是很大

缺點:這種方法的效果比第二種要差一點

3、總結一下

上面提到的三種方法,我實際作過二種,第一種和第二種。第三種沒有作過,因此說的細一點。哈哈。作什麼事都有一個度,超過個度就過變得不好,不能一味的作數據庫服務器集羣,硬件是要花錢買的,也不要一味的分表,分出來1000表,mysql的存儲歸根到底還以文件的形勢存在硬盤上面,一張表對應三個文件,1000個分表就是對應3000個文件,這樣檢索起來也會變的很慢。個人建議是

方法1和方法2結合的方式來進行分表
方法1和方法3結合的方式來進行分表

個人二個建議適合不一樣的狀況,根據我的狀況而定,我以爲會有不少人選擇方法1和方法3結合的方式

未完待續.............

相關文章
相關標籤/搜索