mysql的分區和分表

1,什麼是mysql分表,分區php

什麼是分表,從表面意思上看呢,就是把一張表分紅N多個小表,具體請看mysql分表的3種方法html

什麼是分區,分區呢就是把一張表的數據分紅N多個區塊,這些區塊能夠在同一個磁盤上,也能夠在不一樣的磁盤上mysql

2. mysql分表。linux

一,先說一下爲何要分表程序員

當一張的數據達到幾百萬時,你查詢一次所花的時間會變多,若是有聯合查詢的話,我想有可能會死在那兒了。分表的目的就在於此,減少數據庫的負擔,縮短查詢時間。算法

根據我的經驗,mysql執行一個sql的過程以下:
1,接收到sql;2,把sql放到排隊隊列中 ;3,執行sql;4,返回執行結果。在這個執行過程當中最花時間在什麼地方呢?第一,是排隊等待的時間,第二,sql的執行時間。其實這二個是一回事,等待的同時,確定有sql在執行。因此咱們要縮短sql的執行時間。sql

 

mysql中有一種機制是表鎖定和行鎖定,爲何要出現這種機制,是爲了保證數據的完整性,我舉個例子來講吧,若是有二個sql都要修改同一張表的同一條數據,這個時候怎麼辦呢,是否是二個sql均可以同時修改這條數據呢?很顯然mysql對這種狀況的處理是,一種是表鎖定(myisam存儲引擎),一個是行鎖定(innodb存儲引擎)。表鎖定表示大家都不能對這張表進行操做,必須等我對錶操做完才行。行鎖定也同樣,別的sql必須等我對這條數據操做完了,才能對這條數據進行操做。若是數據太多,一次執行的時間太長,等待的時間就越長,這也是咱們爲何要分表的緣由。數據庫

二,分表服務器

1,作mysql集羣,例如:利用mysql cluster ,mysql proxy,mysql replication,drdb等等架構

有人會問mysql集羣,根分表有什麼關係嗎?雖然它不是實際意義上的分表,可是它啓到了分表的做用,作集羣的意義是什麼呢?爲一個數據庫減輕負擔,說白了就是減小sql排隊隊列中的sql的數量,舉個例子:有10個sql請求,若是放在一個數據庫服務器的排隊隊列中,他要等很長時間,若是把這10個sql請求,分配到5個數據庫服務器的排隊隊列中,一個數據庫服務器的隊列中只有2個,這樣等待時間是否是大大的縮短了呢?這已經很明顯了。因此我把它列到了分表的範圍之內,我作過一些mysql的集羣:

linux mysql proxy 的安裝,配置,以及讀寫分離

mysql replication 互爲主從的安裝及配置,以及數據同步

優勢:擴展性好,沒有多個分表後的複雜操做(php代碼)

缺點:單個表的數據量仍是沒有變,一次操做所花的時間仍是那麼多,硬件開銷大。

2,預先估計會出現大數據量而且訪問頻繁的表,將其分爲若干個表

這種預估大差不差的,論壇裏面發表帖子的表,時間長了這張表確定很大,幾十萬,幾百萬都有可能。 聊天室裏面信息表,幾十我的在一塊兒一聊一個晚上,時間長了,這張表的數據確定很大。像這樣的狀況不少。因此這種能預估出來的大數據量表,咱們就事先分出個N個表,這個N是多少,根據實際狀況而定。以聊天信息表爲例:

我事先建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)

從上面的操做中,我不知道你有沒有發現點什麼?假如我有一張用戶表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)

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

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

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

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

三,總結一下

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

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

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

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

3. mysql的表分區(5.1版本之後)

分表,能夠根據id區間或者時間前後順序等多種規則來分表。分表很容易,然而由此所帶來的應用程序甚至是架構方面的改動工做卻不>容小覷,還包括未來的擴展性等。

在之前,一種解決方案就是使用 MERGE
類型,這是一個很是方便的作飯。架構和程序基本上不用作改動,不過,它的缺點是顯見的:

  • 只能在相同結構的 MyISAM 表上使用
  • 沒法享受到 MyISAM 的所有功能,例如沒法在 MERGE 類型上執行 FULLTEXT 搜索
  • 它須要使用更多的文件描述符
  • 讀取索引更慢

這個時候,MySQL 5.1 中新增的分區(Partition)功能的優點也就很明顯了:

  • 與單個磁盤或文件系統分區相比,能夠存儲更多的數據
  • 很容易就能刪除不用或者過期的數據
  • 一些查詢能夠獲得極大的優化
  • 涉及到 SUM()/COUNT() 等聚合函數時,能夠並行進行
  • IO吞吐量更大

分區容許能夠設置爲任意大小的規則,跨文件系統分配單個表的多個部分。實際上,表的不一樣部分在不一樣的位置被存儲爲單獨的表。

分區應該注意的事項:

一、 作分區時,要麼不定義主鍵,要麼把分區字段加入到主鍵中。

二、 分區字段不能爲NULL,要否則怎麼肯定分區範圍呢,因此儘可能NOT NULL

2、分區的類型

  • RANGE 分區:基於屬於一個給定連續區間的列值,把多行分配給分區。
  • LIST 分區:相似於按RANGE分區,區別在於LIST分區是基於列值匹配一個離散值集合中的某個值來進行選擇。
  • HASH分區:基於用戶定義的表達式的返回值來進行選擇的分區,該表達式使用將要插入到表中的這些行的列值進行計算。這個函數能夠包>含MySQL中有效的、產生非負整數值的任何表達式。
  • KEY分區:相似於按HASH分區,區別在於KEY分區只支持計算一列或多列,且MySQL服務器提供其自身的哈希函數。必須有一列或多列包含>整數值。

能夠經過使用SHOW VARIABLES命令來肯定MySQL是否支持分區,例如:

 

1. range分區

create table t_range( 
  id int(11), 
  money int(11) unsigned not null, 
  date datetime 
  )partition by range(year(date))( 
  partition p2007 values less than (2008), 
  partition p2008 values less than (2009), 
  partition p2009 values less than (2010) 
  partition p2010 values less than maxvalue 
  )

2. list分區

create table t_list( 
  a int(11), 
  b int(11) 
  )partition by list (b)(
  partition p0 values in (1,3,5,7,9), 
  partition p1 values in (2,4,6,8,0) 
  );

3. hash分區

hash分區的目的是將數據均勻的分佈到預先定義的各個分區中,保證各分區的數據量大體一致。

create table t_hash( 
  a int(11), 
  b datetime 
)partition by hash(YEAR(b)) 
  partitions 4;

 其中year(b)對4取模若是是0因此這條數據被分配到了p0分區。

4. key分區

key分區和hash分區類似,KEY分區和HASH分區類似,可是KEY分區支持除text和BLOB以外的全部數據類型的分區,而HASH分區只支持數字分區,KEY分區不容許使用用戶自定義的表達式進行分區,KEY分區使用系統提供的HASH函數進行分區,NDB cluster使用MD5函數來分區,對於其餘存儲引擎mysql使用內部的hash函數,這些函數基於password()同樣的算法。

create table t_key( 
  a int(11), 
  b datetime) 
  partition by key (b) 
  partitions 4;

4. 分表和分區的做用

a),分表後,單表的併發能力提升了,磁盤I/O性能也提升了。併發能力爲何提升了呢,由於查尋一次所花的時間變短了,若是出現高併發的話,總表能夠根據不一樣的查詢,將併發壓力分到不一樣的小表裏面。磁盤I/O性能怎麼搞高了呢,原本一個很是大的.MYD文件如今也分攤到各個小表的.MYD中去了。 

b),mysql提出了分區的概念,我以爲就想突破磁盤I/O瓶頸,想提升磁盤的讀寫能力,來增長mysql性能。 
在這一點上,分區和分表的測重點不一樣,分表重點是存取數據時,如何提升mysql併發能力上;而分區呢,如何突破磁盤的讀寫能力,從而達到提升mysql性能的目的

 

分表,將sql分散到多個表,多個sql同時執行,提升數據庫併發能力,減小每一個表sql的處理時間。

分區,將數據分散到多個區塊兒或者磁盤上,突破磁盤的I/o瓶頸,提升磁盤的讀寫能力

因此數據庫的優化,要分析影響數據庫性能的緣由在哪裏(是cpu忙,仍是磁盤I/o瓶頸),而後對症下藥,而不能糊里糊塗隨便分表,分區。

 

查看sql是否使用分區

explain paititions select id from test where id =123

相關文章
相關標籤/搜索