1. 程式人生 > >簡說mysql分表分割槽

簡說mysql分表分割槽

什麼是資料庫分割槽?

資料庫分割槽是一種物理資料庫設計技術,雖然分割槽技術可以實現很多效果,但其主要目的是為了在特定的SQL操作中減少資料讀寫的總量以縮減響應時間。

分割槽主要有兩種形式://這裡一定要注意行和列的概念(row是行,column是列)

水平分割槽(Horizontal Partitioning) 這種形式分割槽是對錶的行進行分割槽,通過這樣的方式不同分組裡面的物理列分割的資料集得以組合,從而進行個體分割(單分割槽)或集體分割(1個或多個分割槽)。所有在表中定義的列在每個資料集中都能找到,所以表的特性依然得以保持。

舉個簡單例子:一個包含十年發票記錄的表可以被分割槽為十個不同的分割槽,每個分割槽包含的是其中一年的記錄。(注:這裡具體使用的分割槽方式我們後面再說,可以先說一點,一定要通過某個屬性列來分割,譬如這裡使用的列就是年份)

垂直分割槽(Vertical Partitioning) 這種分割槽方式一般來說是通過對錶的垂直劃分來減少目標表的寬度,使某些特定的列被劃分到特定的分割槽,每個分割槽都包含了其中的列所對應的行。

舉個簡單例子:一個包含了大text和BLOB列的表,這些text和BLOB列又不經常被訪問,這時候就要把這些不經常使用的text和BLOB了劃分到另一個分割槽,在保證它們資料相關性的同時還能提高訪問速度。

在資料庫供應商開始在他們的資料庫引擎中建立分割槽(主要是水平分割槽)時,DBA和建模者必須設計好表的物理分割槽結構,不要儲存冗餘的資料(不同表中同時都包含父表中的資料)或相互聯結成一個邏輯父物件(通常是檢視)。這種做法會使水平分割槽的大部分功能失效,有時候也會對垂直分割槽產生影響。

在MySQL 5.1中進行分割槽,MySQL 5.1已經支援分割槽大部分模式:

         Range(範圍)C 這種模式允許DBA將資料劃分不同範圍。例如DBA可以將一個表通過年份劃分成三個分割槽,80年代(1980's)的資料,90年代(1990's)的資料以及任何在2000年(包括2000年)後的資料。

         Hash(雜湊)C 這中模式允許DBA通過對錶的一個或多個列的Hash Key進行計算,最後通過這個Hash碼不同數值對應的資料區域進行分割槽,。例如DBA可以建立一個對錶主鍵進行分割槽的表。

         Key(鍵值)C 上面Hash模式的一種延伸,這裡的Hash Key是MySQL系統產生的。

         List(預定義列表)C 這種模式允許系統通過DBA定義的列表的值所對應的行資料進行分割。例如:DBA建立了一個橫跨三個分割槽的表,分別根據2004年2005年和2006年值所對應的資料。

         Composite(複合模式)- 很神祕吧,哈哈,其實是以上模式的組合使用而已,就不解釋了。舉例:在初始化已經進行了Range範圍分割槽的表上,我們可以對其中一個分割槽再進行hash雜湊分割槽。

    分割槽帶來的好處太多太多了,有多少?俺也不知道,自己猜去吧,要是覺得沒有多少就別用,反正俺也不求你用。不過在這裡俺強調兩點好處:

效能的提升(Increased performance)- 在掃描操作中,如果MySQL的優化器知道哪個分割槽中才包含特定查詢中需要的資料,它就能直接去掃描那些分割槽的資料,而不用浪費很多時間掃描不需要的地方了。需要舉個例子?好啊,百萬行的表劃分為10個分割槽,每個分割槽就包含十萬行資料,那麼查詢分割槽需要的時間僅僅是全表掃描的十分之一了,很明顯的對比。同時對十萬行的表建立索引的速度也會比百萬行的快得多得多。如果你能把這些分割槽建立在不同的磁碟上,這時候的I/O讀寫速度就“不堪設想”(沒用錯詞,真的太快了,理論上100倍的速度提升啊,這是多麼快的響應速度啊,所以有點不堪設想了)了。

對資料管理的簡化(Simplified data management)- 分割槽技術可以讓DBA對資料的管理能力提升。通過優良的分割槽,DBA可以簡化特定資料操作的執行方式。例如:DBA在對某些分割槽的內容進行刪除的同時能保證餘下的分割槽的資料完整性(這是跟對錶的資料刪除這種大動作做比較的)。

此外分割槽是由MySQL系統直接管理的,DBA不需要手工的去劃分和維護。例如:這個例如沒意思,不講了,如果你是DBA,只要你劃分了分割槽,以後你就不用管了就是了。

站在效能設計的觀點上,俺們對以上的內容也是相當感興趣滴。通過使用分割槽和對不同的SQL操作的匹配設計,資料庫的效能一定能獲得巨大提升。下面咱們一起用用這個MySQL 5.1的新功能看看。

如何進行實際分割槽

看看分割槽的實際效果吧。我們建立幾個同樣的MyISAM引擎的表,包含日期敏感的資料,但只對其中一個分割槽。分割槽的表(表名為part_tab)我們採用Range範圍分割槽模式,通過年份進行分割槽:

mysql> CREATE TABLE part_tab

    ->      ( c1 int default NULL,

    -> c2 varchar(30) default NULL,

    -> c3 date default NULL

    ->

    ->     ) engine=myisam

    ->      PARTITION BY RANGE (year(c3)) (PARTITION p0 VALUES LESS THAN (1995),

    ->      PARTITION p1 VALUES LESS THAN (1996) , PARTITION p2 VALUES LESS THAN (1997) ,

    ->      PARTITION p3 VALUES LESS THAN (1998) , PARTITION p4 VALUES LESS THAN (1999) ,

    ->      PARTITION p5 VALUES LESS THAN (2000) , PARTITION p6 VALUES LESS THAN (2001) ,

    ->      PARTITION p7 VALUES LESS THAN (2002) , PARTITION p8 VALUES LESS THAN (2003) ,

    ->      PARTITION p9 VALUES LESS THAN (2004) , PARTITION p10 VALUES LESS THAN (2010),

    ->      PARTITION p11 VALUES LESS THAN MAXVALUE );

Query OK, 0 rows affected (0.00 sec)

注意到了這裡的最後一行嗎?這裡把不屬於前面年度劃分的年份範圍都包含了,這樣才能保證資料不會出錯,大家以後要記住啊,不然資料庫無緣無故出錯你就爽了。那下面我們建立沒有分割槽的表(表名為no_part_tab):

mysql> create table no_part_tab

    -> (c1 int(11) default NULL,

    -> c2 varchar(30) default NULL,

    -> c3 date default NULL) engine=myisam;

Query OK, 0 rows affected (0.02 sec)

下面寫一個儲存過程,它能向咱剛才建立的已分割槽的表中平均的向每個分割槽插入共8百萬條不同的資料。填滿後,咱就給沒分割槽的克隆表中插入相同的資料:

mysql> delimiter //

mysql> CREATE PROCEDURE load_part_tab()

    -> begin

    -> declare v int default 0;

    ->  while v < 8000000

    -> do

    -> insert into part_tab

    -> values (v,'testing partitions',adddate('1995-01-01',(rand(v)*36520) mod 3652));

    -> set v = v + 1;

    -> end while;

    -> end

    ->

Query OK, 0 rows affected (0.00 sec)

mysql> delimiter ;

mysql> call load_part_tab();

Query OK, 1 row affected (8 min 17.75 sec)

mysql> insert into no_part_tab select * from part_tab;

Query OK, 8000000 rows affected (51.59 sec)

Records: 8000000 Duplicates: 0 Warnings: 0

表都準備好了。咱開始對這兩表中的資料進行簡單的範圍查詢吧。先分割槽了的,後沒分割槽的,跟著有執行過程解析(MySQL Explain命令解析器),可以看到MySQL做了什麼:

mysql> select count(*) from no_part_tab where

    -> c3 > date '1995-01-01' and c3 < date '1995-12-31';

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

| count(*) |

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

|   795181 |

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

1 row in set (38.30 sec)

mysql> select count(*) from part_tab where

    -> c3 > date '1995-01-01' and c3 < date '1995-12-31';

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

| count(*) |

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

|   795181 |

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

1 row in set (3.88 sec)

mysql> explain select count(*) from no_part_tab where

    -> c3 > date '1995-01-01' and c3 < date '1995-12-31'\G

*************************** 1. row ***************************

                  id: 1

 select_type: SIMPLE

        table: no_part_tab

         type: ALL

possible_keys: NULL

          key: NULL

      key_len: NULL

          ref: NULL

         rows: 8000000

        Extra: Using where

1 row in set (0.00 sec)

mysql> explain partitions select count(*) from part_tab where

    -> c3 > date '1995-01-01' and c3 < date '1995-12-31'\G

*************************** 1. row ***************************

           id: 1

 select_type: SIMPLE

        table: part_tab

   partitions: p1

         type: ALL

possible_keys: NULL

          key: NULL

      key_len: NULL

          ref: NULL

         rows: 798458

        Extra: Using where

1 row in set (0.00 sec)

從上面結果可以容易看出,設計恰當表分割槽能比非分割槽的減少90%的響應時間。而命令解析Explain程式也告訴我們在對已分割槽的表的查詢過程中僅對第一個分割槽進行了掃描,其他都跳過了。

對Vertical Partitioning的一些看法

雖然MySQL 5.1自動實現了水平分割槽,但在設計資料庫的時候不要輕視垂直分割槽。雖然要手工去實現垂直分割槽,但在特定場合下你會收益不少的。例如在前面建立的表中,VARCHAR欄位是你平常很少引用的,那麼對它進行垂直分割槽會不會提升速度呢?咱們看看測試結果:

mysql> desc part_tab;

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

| Field | Type        | Null | Key | Default | Extra |

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

| c1    | int(11)     | YES |     | NULL    |       |

| c2    | varchar(30) | YES |     | NULL    |       |

| c3    | date        | YES |     | NULL    |       |

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

3 rows in set (0.03 sec)

mysql> alter table part_tab drop column c2;

Query OK, 8000000 rows affected (42.20 sec)

Records: 8000000 Duplicates: 0 Warnings: 0

mysql> desc part_tab;

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

| Field | Type    | Null | Key | Default | Extra |

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

| c1    | int(11) | YES |     | NULL    |       |

| c3    | date    | YES |     | NULL    |       |

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

2 rows in set (0.00 sec)

mysql> select count(*) from part_tab where

    -> c3 > date '1995-01-01' and c3 < date '1995-12-31';

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

| count(*) |

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

|   795181 |

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

1 row in set (0.34 sec)

在設計上去掉了VARCHAR欄位後,不止是你,發現查詢響應速度上獲得了另一個90%的時間節省。所以大家在設計表的時候,一定要考慮,表中的欄位是否真正關聯,又是否在你的查詢中有用?

補充說明

支援所有儲存引擎(MyISAM, Archive, InnoDB, 等等)

對分割槽的表支援索引,包括本地索引local indexes,對其進行的是一對一的檢視映象,假設一個表有十個分割槽,那麼其本地索引也包含十個分割槽。

關於分割槽的元資料Metadata的表可以在INFORMATION_SCHEMA資料庫中找到,表名為PARTITIONS。

All SHOW 命令支援返回分割槽表以及元資料的索引。

對其操作的命令和實現的維護功能有(比對全表的操作還多):

ADD PARTITION

DROP PARTITION

COALESCE PARTITION

REORGANIZE PARTITION

ANALYZE PARTITION

CHECK PARTITION

OPTIMIZE PARTITION

REBUILD PARTITION

REPAIR PARTITION