MySQL深入學習(4)——分區表

1. 概述

    分區功能其實並不是在存儲引擎層完成的,所以並不是只有InnoDB存儲引擎支持分區,常見的存儲引擎MyISAM、NDB等都支持。但也並不是所有的存儲引擎都支持。

    分區是將一個表的數據按照某種方式,比如按照時間上的月份,分成多個較小的,更容易管理的部分,但是邏輯上仍是一個表。所以,分區跟性能沒有必然關係,分區更多的是從管理的角度出發的。

    MySQL數據庫在5.1版本增加了對分區的支持。分區的過程是將一個表或者索引分解爲多個更小、更易於管理的部分。以數據庫查詢來說,從邏輯上講只有一個表或一個索引,但是在物理上這個表或者索引可能會有數十個物理分區組成。每個分區都是獨立的對象,可以獨自處理,也可以作爲一個更大的對象的一部分進行處理。

    MySQL數據庫支持水平分區,而不支持垂直分區(和水平分表、垂直分表的概念差不多)。此外,MySQL支持的是局部分區索引,即一個分區既存放數據又存儲索引。可以通過以下指令查看數據庫是否啓用了分區功能。

show variables like '%partition%';
或者
show pulgins;

    上面說過,分區與性能其實並沒有必然聯繫,雖然說分區確實會爲某些SQL語句帶來性能提升,但是分區的主要目的是用於數據庫高可用性的管理。在OLTP(線上事務處理)應用中,對於分區的使用應該非常小心。目前MySQL支持的分區類型如下:

(1)RANGE分區:行數據基本屬於一個給定連續區間的列值被放入區間。

(2)LIST分區:和RANGE分區類型相似,但是LIST分區面向的是離散的值。

(3)HASH分區:根據用戶自定義的表達式的返回值來進行分區,返回值不能爲負數。

(4)Key分區:根據MySQL數據庫提供的哈希函數來進行分區。

   不論是何種分區類型,如果表中存在主鍵或唯一索引的列,則分區列必須是主鍵或唯一索引的一部分。唯一索引列可以是null值。在沒有主鍵和唯一索引的表中可以指定任意列爲索引列。表中只能最多有一個唯一索引,即primary key 和unique key不能同時存在,primary key是包含unique key特性的。而且以上幾種分區類型的分區字段必須爲int類型字段或者可以轉換爲int類型。

 

2. RANGE分區

    RANGE分區的分區字段必須是整型或者轉換爲整型,按照字段的區間劃分數據的歸屬,典型的就是按照時間維度的月份分區。比如下表就是一個創建一句id列的區間分區表,當id的值小於10時,數據插入p1分區,大於等於10小於20時,則存儲到p2分區。

CREATE TABLE test_range_partition(
    id INT auto_increment,
    createdate DATETIME,
    primary key (id,createdate)
) 
PARTITION BY RANGE (id ) (
   PARTITION p1 VALUES LESS THAN ( 10 ),
   PARTITION p2 VALUES LESS THAN ( 20 )
);

    表被分區處理後,表在磁盤上的物理文件就不再是一個ibd文件組成了,而是由建立分區時的各個分區ibd文件組成。比如上面的表會被分爲 test_range_partition#P#p1.ibd和 test_range_partition#P#p2.ibd兩個文件。

    上面定義了分區的區間有兩種,一種是id小於10的,另一種是10到20之間的,那如果插入的行記錄的id值大於20呢?MySQL對於插入一個不在分區中定義的值的時候,MySQL會拋出一個異常。

    實際上,對於分區依據字段並不是必須爲int類型數據,我麼你需要保證的是在RANGE() 這個括號內的數據是一個int類型數據即可,所以我們可以將一些非int類型的字段也作爲分區依據,比如上表中的createdate字段,可以通過Year(createdate)作爲分區依據。新分區規則如下所示

CREATE TABLE test_range_partition(
    id INT auto_increment,
    createdate DATETIME,
    primary key (id,createdate)
) 
PARTITION BY RANGE (YEAR(createdate) ) (
   PARTITION p2008 VALUES LESS THAN ( 2009 ),
   PARTITION p2009 VALUES LESS THAN ( 2010 )
);

    爲表添加分區之後,確實會對於SQL語句有着很好的性能提升,比如在上面的依據時間分區的表中進行下面SQL查詢

select * from test_range_partition where createdate = '2018-10-10';

在上面的SQL語句中,SQL執行時(可以通過explain 來查看MySQL中的SQL語句執行計劃,在partitions這一欄就可以看到查詢的分區)只會在p2008這個分區中進行查找,而不是在所有分區,或者說整張表中進行查詢,查詢速度自然大幅提升。

3. LIST分區

    LIST分區和RANGE分區非常類似,只是區別在於LIST分區中所依據的列(字段)的值是散列的,或者說不連續的,隨機的。比如

create table t1 (
    id int,
    age int
)partition by list(id) (
    partition p1 values in (2,4,1,5,7),
    partition p2 values in (0,3,9,8,6)
)

    注意RANGE分區定義使用的是values less than語句,而LIST分區定義語句使用的是values in。如果插入的數據的值不在分區定義中,那麼同樣會拋出異常。

    LIST分區要求有確定的固定的散列值,所以通常都是用於哪些有明確的取值範圍的數據字段,比如說性別字段,0代表女,1代表男。

4. HASH分區

    HASH分區的目的是將數據均勻的分佈到各個預先定義的分區中,保證各個分區的數據量接近一致。在RANGE和LIST分區中,需要給定一個明確的列值或者範圍,而在HASH分區中,MySQL自動完成這些工作,用戶所需要進行的只是基於要進行哈希分區的列值指定一個列值或表達式,以及被分區的表將要被分割成的分區數量。如下所示

create table t2 (
    id int,
    date datetime,
    primary key(id,date)
) partition by hash(id)
partition 4;

create table t2 (
    id int,
    date datetime,
    primary key(id,date)
) partition by hash(year(date))
partition 4;

    同樣的,HASH分區的所依據的內容並不是必須爲int字段,只要by hash(expr)中的expr是整形數據即可,哪怕是一個表達式。而partition num就表示分爲num個分區,如果不包含這個partition子句,那麼分區數量默認爲1。

5. KEY分區

    key分區和hash分區類似,但不同之處在於key分區不需要指定int類型的字段或者是將字段值處理爲int型在進行分區,而是採用了MySQL的內置hash算法,對於大部分的字段類型都支持直接分區。比如說

create table t3 (
    id int,
    date datetime,
    primary key(id,date)
) partition by key(date)
partition 4;

KEY分區支持除text和BLOB之外的所有數據類型的分區,而HASH分區只支持數字分區,KEY分區不允許使用用戶自定義的表達式進行分區,KEY分區使用系統提供的HASH函數進行分區。

6. COLUMNS分區

    在上面幾種分區中,都對分區依賴的數據類型做了限制,必須是int型或者是能通過YEAR()等函數處理爲int型數據。但從MySQL5.5開始支持COLUMNS分區,該分區可以視爲RANGE和LIST分區的進化,分區一句類型直接比較而得,不需要轉換爲整形數據,此外Columns分區可以依據多個列的值進行分區。

    其支持的數據類型包括:

(1)所有整形類型,比如int、bigint等,但是float和decimal不支持。

(2)日期類型只支持date和datetime。

(3)字符串類型只支持char、varchar、binary、varbinary。blob和text不支持。

7. 子分區

    所謂子分區其實就是在一個分區的基礎上在進行一次分區,或者叫複合分區。MySQL數據庫允許在RANGE和LIST的分區上在進行HASH和KEY的子分區,比如:

create table t5 (
    id int,
    date datetime
    primary key(id,date)
)partition by range(year(date)) 
    subpartition by hash(to_days(date))
    subpartition 4
(partition p1 values less than (2010),
partition p2 values less than (2020)
);

8. 分區和性能

    上面說過分區確實可以爲數據SQL執行帶來性能提升,但是並不是一定會帶來性能提升,甚至會帶來性能下降(和數據庫索引有些類似)。可以來做一個簡單的分析,假如有一張1000萬行數據的表,我們對其主鍵進行分區處理,分爲10個分區,每個分區由100萬行數據。

    如果執行select * from table where pk = '**',我們都知道MySQL數據庫內部由B+樹索引組織數據,主要影響查詢性能的是IO次數,如果樹的高度爲2,那麼就需要進行兩次IO。所以,如果100萬行數據和1000萬行數據在數據庫中的樹高度都爲2,那麼其實分區與不分區的性能差異並不大,如果說100萬行數據是2層樹結構,而1000萬行數據是3層樹結構,那麼確實會多出一次IO操作的性能差異。

   那麼,假如確實100萬行數據是2層樹結構,而1000萬行數據是3層樹結構,但是,我們對一個表的查詢並不總是依據主鍵查詢,如果我們執行另一條SQL語句不涉及主鍵查詢的話,比如select * from table where key = '**'。該條SQL語句的執行必然會涉及到掃描10個分區的所有數據,那麼一個分區2次IO,10個分區就是20次IO,而在原來的單表不分區的設計也只是3次IO而已。

    在OLTP(線上事務處理)類型的應用中,比如電商平臺、博客網站、網絡遊戲等,通常都會對一張表中的每個字段都有可能進行查詢操作,而且每次查詢的數據量都很少;而對於OLAP(在線數據分析)類型的應用中,比如數據倉庫等,通常就會依據一個固定的字段進行查詢取數據,而且每次提取都是大量數據提取。

    所以,在設計分區時,尤其是對於OLTP的應用中,一定要慎重使用分區。

 

 

 

 

    

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