1. 程式人生 > >MySQL中的分割槽是什麼?為什麼要分割槽?有什麼好處?怎麼進行分割槽?

MySQL中的分割槽是什麼?為什麼要分割槽?有什麼好處?怎麼進行分割槽?

本文轉載自:https://www.2cto.com/database/201805/742843.html

MySQL從5.1版本開始支援分割槽功能,它允許可設定的一定邏輯,跨檔案系統分配單個表的多個部分,但是就訪問資料庫而言,邏輯上還是隻有一個表。

還是老樣子,在學習新知識之前都先帶著問題去尋找想要的答案:

1、什麼是分割槽?

2、為什麼分割槽?好處在哪?

3、如何分割槽?

什麼是MySQL分割槽?

一開始也講了,根據一定邏輯規則,將一個表拆成多個更小更容易管理的部分。例如我們新建一張表利用range分割槽

\

\

邏輯上還是隻有一張表,但是實際上確有3個物理分割槽物件組成

我們檢視是否支援分割槽時對於5.6以下的版本可以使用如下命令:

1

show variables like '%partition%';

而在5.6及以上用如上命令會顯示empty set,但是並不是表示不支援分割槽,而是我們應該這樣檢視:

1

show plugins;

當看到有partition並且status是active時表示支援。

為什麼分割槽?

1、分割槽可以在一個表中儲存比單個磁碟或檔案系統分割槽上的資料更多的資料,因為我們可以將分割槽表儲存在不同物理磁碟上

2、對已過期或者不需要儲存的資料,可以通過刪除與這些資料有關的分割槽來快速刪除資料,他的效率遠比delete高;

3、優化查詢,在where子句中包含分割槽條件時,可以只掃描必要的一個或者多個分割槽來提高查詢效率;例如下面語句:

SELECT * FROM t PARTITION(p0,p1)WHERE c <5僅選擇與WHERE條件匹配的分割槽p0和p1中的那些行。

在這種情況下,MySQL不檢查表t的任何其他分割槽;

4、涉及聚合函式SUM()、COUNT()的查詢時,可以容易的在每個分割槽上並行處理,例如在執行下面這條語句:

SELECT salesperson_id,COUNT(orders)as order_total FROM sales GROUP BY salesperson_id ;會在每個分割槽上都同時執行查詢;

5、憑藉在多個磁碟上傳播資料,實現更高的查詢吞吐量。

說這麼多好處,也說一下它的缺點吧:

1、一個表最多隻能有1024個分割槽;

2、在MySQL5.1中,分割槽表示式必須為整數或者返回整數,而在MySQL5.5以後可以使用非整數,即其他的資料型別(並不是所有的資料型別)來分割槽;

3、同一個分割槽表的所有分割槽必須使用相同儲存引擎;

4、分割槽表無法使用外來鍵約束;

1

> 1506 - Foreign keys are not yet supported in conjunction with partitioning

如何分割槽?

有以下四種分割槽型別:

RANGE分割槽:基於一個給定連續區間範圍,把資料分配到不同的分割槽;

LIST分割槽:類似RANGE分割槽,區別在LIST分割槽是基於枚舉出的值列表分割槽,RANGE是基於給定連續區間範圍分割槽;

HASH分割槽:基於使用者定義的表示式返回值來選擇分割槽,該表示式對要插入到表的行中列值操作;

KEY分割槽:類似HASH,但是HASH允許使用使用者自定義表示式,而KEY分割槽不允許,它需要使用MySQL伺服器提供的HASH函式,同時HASH分割槽只支援整數分割槽,而KEY分割槽支援除BLOB和TEXT型別外其他列;

但是無論是哪一種分割槽型別,要麼分割槽表上沒有主鍵/唯一鍵,要麼分割槽表的主鍵/唯一鍵都必須包含分割槽鍵,否則會報錯:

1

A PRIMARY KEY must include all columns in the table's partitioning function

1

A UNIQUE INDEX must include all columns in the table's partitioning function

RANGE分割槽:利用取值範圍將資料分割槽,區間要連續並且不可以重疊,使用VALUES LESS THAN 進行分割槽定義,舉例如下:

分別建立兩張表,分割槽的沒有分割槽的:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

create table testwithoutpartition(

id int DEFAULT null,

name char(5),

datedata date

)

 

create table testwithpartition(

id int DEFAULT null,

name char(5),

datedata date

)

PARTITION BY RANGE (year(datedata)) (

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 MAXVALUE );

上面分割槽語句的VALUES LESS THAN MAXVALUE子句是表示當有大於2000的時候都插入到p6中,MAXVALUE代表最大可能整數值,否則當我們插入一條記錄包含2001時會報錯;

從上面我們可以看到順序是有規定的,當我們把p2設為1998而p3設為1997時會報錯:

1

VALUES LESS THAN value must be strictly increasing for each partition

接著我們使用儲存過程分別給兩張表插入資料

1

2

3

4

5

6

7

8

9

CREATE DEFINER=`root`@`localhost` PROCEDURE `insertdata`()

BEGIN

    DECLARE numb int DEFAULT 0;

    while i < 20000

    do

        insert into testwithpartition values(numb,'lanco',DATE_ADD('1996-04-01',INTERVAL numb DAY));

        set numb =numb+1;

    end while;

END

當要刪除過期的資料時候,只需要簡單的語句如此來刪除p0分割槽中的資料:

1

ALTER TABLE testwithpartition drop PARTITION p0

LIST分割槽:建立離散值列表指定特定值屬於哪一個分割槽

1

2

3

4

5

6

7

8

9

10

11

create table testwithlistpartition(

id int not null,

name char(5),

category VARCHAR(30)

)

PARTITION BY LIST (id) (

PARTITION p0 VALUES IN (1,5) ,

PARTITION p1 VALUES IN (11,15) ,

PARTITION p2 VALUES IN (6,10) ,

PARTITION p3 VALUES IN (16,20)

);

\

如上,可以看出和RANGE分割槽不同的是,我們不必遵循特定的順序,而如果我們試圖插入的記錄不在分割槽值列表中,他不像RANGE有VALUES LESS THAN MAXVALUE這樣包含其它的值方式,

\

COLUMNS分割槽:

他是在MySQL5.5引入的分割槽型別,實際上就是為了解決RANGE和LIST分割槽只支援整數分割槽問題,COLUMNS可以細分為RANGE?COLUMNS和LIST?COLUMNS分割槽,他們都支援整數,日期時間,字串三大資料型別:

1、所有的整數型別,int,tinyint,bigint等,但不支援decimal,float等;

2、日期時間型別:date和datetime;

3、字元型別:char,varchar,binary,varbinary;不支援text和blob型別;

但是要注意,Columns分割槽僅支援一個或者多個欄位名作分割槽鍵,而不支援表示式作分割槽鍵,如上面的RANGE分割槽year(datadate),即使返回整型也不可以;

但是其中一大亮點是能夠支援多列分割槽:

RANGE COLUMNS

我們先建立一個RANGE分割槽:

1

2

3

4

5

6

7

8

CREATE TABLE test1 (

    a INT,

    b INT

)

PARTITION BY RANGE COLUMNS(a, b) (

    PARTITION p0 VALUES LESS THAN (5, 12),

    PARTITION p3 VALUES LESS THAN (MAXVALUE, MAXVALUE)

);

那這樣子我插入三條記錄id=5,那他是插入到哪裡呢?

1

INSERT INTO test1 VALUES (5,10), (5,11), (5,12);

我們如何檢視他是插入到哪一個分割槽:

1

2

3

4

5

6

7

8

9

select partition_name part,

partition_expression expr,

partition_description descr,

table_rows

from

information_schema.partitions

where

TABLE_SCHEMA = schema()

and table_name='test1';

\

如圖可以看出是插入到p1分割槽表裡;繼續寫入一條記錄,並且發現是寫入到p3分割槽表裡,

接著我們建立一個RANGE COLUMNS分割槽表:

1

2

3

4

5

6

7

8

CREATE TABLE rc1 (

    a INT,

    b INT

)

PARTITION BY RANGE COLUMNS(a, b) (

    PARTITION p0 VALUES LESS THAN (5, 12),

    PARTITION p3 VALUES LESS THAN (MAXVALUE, MAXVALUE)

);

依舊和test1表一樣的三條資料插入,檢視插入到哪裡:

\

可以看出來兩者對於資料插入的比較差別了嗎?

這是因為我們比較的是元組值而非標量值,你看

\

例如我們這裡插入的記錄(5,10),它先取第一個值5與分割槽限制行值比較,因為p0(5,12),記錄5不小於限制行值5,所以比較記錄第二位10,他小於限制行值12,所以屬於p0區,

假如我們又插入新資料(2,13),檢視可知:

載入中...

還是p0區,如此便清晰明瞭,它先用插入的資料的第一個欄位值和分割槽的第一個值進行比較,如果插入的第一個值小於分割槽的第一個值那麼就不需要比較第二個值就屬於該分割槽;如果第一個值等於分割槽的第一個值,開始比較第二個值同樣如果第二個值小於分割槽的第二個值那麼就屬於該分割槽。

LIST COLUMNS:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

CREATE TABLE test3 (

    first_name VARCHAR(25),

    last_name VARCHAR(25),

    street_1 VARCHAR(30),

    street_2 VARCHAR(30),

    city VARCHAR(15),

    renewal DATE

)

PARTITION BY LIST COLUMNS(city) (

    PARTITION pRegion_1 VALUES IN('Oskarshamn', 'H?gsby', 'M?nster?s'),

    PARTITION pRegion_2 VALUES IN('Vimmerby', 'Hultsfred', 'V?stervik'),

    PARTITION pRegion_3 VALUES IN('N?ssj?', 'Eksj?', 'Vetlanda'),

    PARTITION pRegion_4 VALUES IN('Uppvidinge', 'Alvesta', 'V?xjo')

);

和RANGE COLUMNS一樣,支援其他資料型別作分割槽鍵;

HASH分割槽:

基於給定的分割槽個數,將資料分配到不同分割槽,HASH分割槽只能對整數進行分割槽,對於非整型欄位只能通過表示式轉為整型,MySQL支援兩種HASH分割槽-常規hash和線性hash。

例如建立如下hash分割槽表:

1

2

3

4

5

6

7

8

CREATE TABLE employees (

    id INT NOT NULL,

    fname VARCHAR(30),

    lname VARCHAR(30),

    hired DATE NOT NULL DEFAULT '1970-01-01'

)

PARTITION BY HASH( id )

PARTITIONS 4;

 

它表示根據id值hash分割槽,分割槽數為4,如果不包含PARTITIONS子句,則分割槽數預設為1。

insert into employees 

values(1,'liu','lanco','2018-05-01'),

(2,'liu','lanco','2018-05-02'),(3,'liu','lanco','2018-05-03'),(4,'liu','lanco','2018-05-04'),(5,'liu','lanco','2018-05-05');

檢視分割槽範圍:

\

實際上常規的hash是基於取模運算進行判斷新記錄應該插入到哪一個分割槽,例如上面新記錄id=2,運算表示式為mod(2,4)

常規的hash分割槽看起來挺不錯的,但是當我們需要增加分割槽或者合併分割槽時候,問題就來了,這裡有4個分割槽,現在需要增加一個分割槽,或者合併分割槽,原來的取模演算法是MOD(expr,4),現在新增一個分割槽,取模演算法變成MOD(expr,5),資料都要重新計算分割槽,這個代價實在太大了。

線性HASH分割槽:

線性HASH分割槽與常規HASH分割槽不同之處在於線性HASH使用線性的2的冪運演算法則,並且分割槽關鍵字為LINEAR HASH。

1

2

3

4

5

6

7

8

CREATE TABLE employees (

    id INT NOT NULL,

    fname VARCHAR(30),

    lname VARCHAR(30),

    hired DATE NOT NULL DEFAULT '1970-01-01'

)

PARTITION BY HASH( id )

PARTITIONS 4;

假設將要儲存的記錄分割槽編號為N,num是分割槽數,我們可以通過如下計算得到指定記錄儲存在哪一個分割槽:

1、找到下一個大於等於num的2次冪,這個值設為V ,可以通過如下公式

V =POWER(2,Ceilling(Log(2,num)));

2、其次,設定N=F(column_list)&(V-1)

例如上述給出的例子num=4,根據1的式子計算得出V=4,現在要插入一條新紀錄,id=234;現在來計算它對應的N值,代入這裡給的式子N=234&(4-1)=2;

3、當N>=num時,設定V=Ceiling(V/2),設定N=N&(V-1),由於id=234這條記錄,N=2<4所以可以判斷它在第三個分割槽。我們通過執行計劃可以看到他的確是被分配到p2分割槽

這裡要注意,分割槽從p0開始的,就像取模

\

KEY分割槽:

和HASH分割槽類似,只是HASH分割槽允許使用使用者自定義表示式,而KEY分割槽不允許使用使用者自定義表示式,需要使用MySQL伺服器提供的HASH函式,同時HASH分割槽只支援整數分割槽,而KEY分割槽支援使用除了BLOB,TEXT之外其他型別列作分割槽鍵。

與HASH分割槽不同,建立key分割槽時候可以不指定分割槽鍵,預設會選擇主鍵作為分割槽鍵,在沒有主鍵情況下會選擇非空唯一鍵作分割槽鍵,下面例子便是使用id作為分割槽鍵(雖然沒有明確指出)

1

2

3

4

5

6

7

CREATE TABLE k1 (

    id INT NOT NULL,

    name VARCHAR(20),

    UNIQUE KEY (id)

)

PARTITION BY KEY()

PARTITIONS 2;

另外一點,在KEY分割槽使用LINEAR關鍵字會與LINEAR HASH有同樣效果。

檢視文件之後補充一個文件的知識點:對NULL值的處理

MySQL不禁止在分割槽鍵值上使用NULL,一般情況下都會把它當成0或者最小值處理。

RANGE分割槽處理NULL值:

上面我們已經建立了使用range分割槽的表test1,現在往其中插入null值:
載入中...

通過執行計劃可以發現是被當做最小值處理,所以寫入到最小p1分割槽中(我的表定義p1分割槽儲存最小資料)。

LIST分割槽處理NULL值:

當我們插入一條包含null值時候,會報如下錯誤:

\

當我們修改分割槽增加上null定義之後就可以寫入。

HASH分割槽處理NULL:

還是一樣對上述所建hash分割槽的表employees使用執行計劃,可以看出在hash分割槽裡把它當做零看待。

\