MySQL學習筆記十三:表分區


1.分區一般用於非常大的表,采用“分而治之”的策略,將一個很大的對象分成多個小對象進行管理,每個分區都是一個獨立的對象。

分區使用分區鍵將數據根據范圍值,特定列值或HASH值等規則分布在不同的分區中。查看當前MySQL是否支持分區,如下所示。

mysql> show variables like '%partition%'; --或者使用select @@have_partitioning;
+-------------------+-------+
| Variable_name     | Value |
+-------------------+-------+
| have_partitioning | YES   |
+-------------------+-------+
1 row in set (0.00 sec)

2.分區類型,主要分為以下四種:

RANGE:基於一個連續的區間范圍,將數據分配到不同的分區。

LIST:基於枚舉出的值列表分區

HASH:基於給定的分區數,將數據分配到不同的分區

KEY:類似於HASH分區,但不允許用戶自定義表達式

3.設置表的分區,類似指定表的存儲引擎,分區不支持CSV和merge引擎對象,如下所示

create table student(
    sno int not null,
    sname varchar(30) not null,
    sclass varchar(10) not null,
    sage int not null,
    sgender varchar(6)
)engine=InnoDB default charset=utf8
partition by list columns(sgender)(
    partition p0 values in (''),
    partition p1 values in ('')
);

不管哪種分區類型,分區鍵必須是主鍵或唯一鍵,除非兩者都沒有,否者將會報如下錯誤。

Error Code: 1503. A PRIMARY KEY must include all columns in the table's partitioning function

4.RANGE分區

RANGE分區利用取值范圍來進行分區,區間必須連續且不重疊,使用values less than進行分區定義,分區鍵必須是INT,或者表達式返回INT。

create table users_par(
    id int not null,
    usrName varchar(50) not null,
    usrEmail varchar(50) not null,
    age int not null,
    regDate date not null
)engine=InnoDB default charset=utf8
partition by range(age)(
    partition p0 values less than(20),
    partition p1 values less than(30),
    partition p2 values less than maxvalue --防止插入大於30歲的用戶,出現錯誤
);

如果是將注冊日期作為分區鍵,則須要使用日期處理函數轉換為整型,例如year(regDate),to_days(regDate),to_seconds(regDate),且只支持這三個函數。

或者使用RANGE COLUMNS分區,則不需要轉換日期,如下所示

create table users_par(
    id int not null,
    usrName varchar(50) not null,
    usrEmail varchar(50) not null,
    age int not null,
    regDate date not null
)engine=InnoDB default charset=utf8
partition by range columns(regDate)(
    partition p0 values less than('2005-05-05'),
    partition p1 values less than('2009-09-09'),
    partition p2 values less than('2015-05-05'),
);

RANGE分區特別適用於刪除過期數據或者某范圍數據,只需要alter table tbl_name truncate partition partition_name即可,比delete語句效率要高很多,還有就是經常使用分區鍵的查詢,可以提高查詢性能,因為只需掃描某些分區就OK,如下所示。

mysql> explain partitions select * from users_par where age=30;
+----+-------------+-----------+------------+------+---------------+------+---------+------+------+-------------+
| id | select_type | table     | partitions | type | possible_keys | key  | key_len | ref  | rows | Extra       |
+----+-------------+-----------+------------+------+---------------+------+---------+------+------+-------------+
|  1 | SIMPLE      | users_par | p2         | ALL  | NULL          | NULL | NULL    | NULL |    2 | Using where |
+----+-------------+-----------+------------+------+---------------+------+---------+------+------+-------------+

5.LIST分區

LIST分區是建立離散的值列表來實現特定的值屬於哪個分區,使用partition by list來實現,values in 來定義。

CREATE TABLE `student` (
 `sno` int(11) NOT NULL,
 `sname` varchar(30) NOT NULL,
 `sclass` varchar(10) NOT NULL,
 `sage` int(11) NOT NULL,
 `sgender` varchar(6) DEFAULT NULL
)ENGINE=InnoDB DEFAULT CHARSET=utf8
PARTITION BY LIST  COLUMNS(sgender)(
PARTITION p0 VALUES IN (''),
PARTITION p1 VALUES IN ('')
);

如果試圖插入的值不在分區值列表中,插入語句將會報錯,將要匹配的值必須在分區值列表中找到。

6.COLUMNS分區

columns分區是對range,list分區的補充,彌補了后兩者只支持整型數分區(或者通過轉換為整型數),使得支持數據類型增加很多(所有整數類型,日期時間類型,字符類型),還支持多列分區。

columns分區可細分為range columns分區和list columns分區,多列分區示例:

create table range_columns(
    a int not null,
    b int not null
)
partition by range columns(a,b)(
    partition p0 values less than(0,10),
    partition p1 values less than(10,20),
    partition p2 values less than(10,30),
    partition p3 values less than(maxvalue,maxvalue)
);

在多列分區表上插入數據時,采用元組的比較,即多列排序,先根據field1排序,再根據field2排序,根據排序結果來來分區存儲數據。

mysql> insert into range_columns values (0,9);
Query OK, 1 row affected (0.00 sec)

mysql> select  PARTITION_NAME,PARTITION_EXPRESSION,TABLE_ROWS from information_schema.partitions where table_schema=schema() and table_name='range_columns';
+----------------+----------------------+------------+
| PARTITION_NAME | PARTITION_EXPRESSION | TABLE_ROWS |
+----------------+----------------------+------------+
| p0             | `a`,`b`              |          1 |
| p1             | `a`,`b`              |          0 |
| p2             | `a`,`b`              |          1 |
| p3             | `a`,`b`              |          0 |
+----------------+----------------------+------------+
4 rows in set (0.01 sec)

7.HASH分區

HASH主要是為了讓數據在設定個數的分區中盡可能分布平均,執行哈希分區時,mysql會對分區鍵執行哈希函數,以確定數據放在哪個分區中。HASH分區分為常規HASH分區和線性HASH分區,前者使用取模算法,后者使用線性2的冪的運算規則。HASH分區示例如下,

create table hash_par(
    id int not null,
    name varchar(50) not null
)
partition by hash(id) partitions 4; ----如果要指定為線性hash,可以使用partition by linear hash

插入一個id為31的數據,如下所示

mysql> insert into hash_par values (31,'zhumuxian');
Query OK, 1 row affected (0.00 sec)

mysql> explain partitions select * from hash_par where id=31;
+----+-------------+----------+------------+------+---------------+------+---------+------+------+-------------+
| id | select_type | table    | partitions | type | possible_keys | key  | key_len | ref  | rows | Extra       |
+----+-------------+----------+------------+------+---------------+------+---------+------+------+-------------+
|  1 | SIMPLE      | hash_par | p3         | ALL  | NULL          | NULL | NULL    | NULL |    2 | Using where |
+----+-------------+----------+------------+------+---------------+------+---------+------+------+-------------+
1 row in set (0.00 sec)

HASH分區盡可能讓數據平均地分布在每個分區上,提高了查詢效率,但是增加了分區管理的代價,比如以前有5個分區,現在要加上一個分區,算法有mod(expr,5)變成(expr,6),原來5個分區的數據大部分要重新結算重新分區。雖然使用線性HASH分區會降低分區管理的代價,但是數據卻沒有常規HASH分布得那么均勻。

8.KEY分區

KEY分區類似與HASH分區,但是不能自定義表達式,不過支持分區鍵的類型很多,除卻Text,Blob等文本類型。KEY分區的設置如下:

create table hash_par(
    id int not null,
    name varchar(50) not null
)
partition by key(id) partitions 4;

KEY分區的分區鍵可以不指定,默認會使用表的主鍵作為分區鍵,如果沒有主鍵,就使用唯一鍵,兩者都沒有的話就必須指定分區鍵了。

9.子分區(關鍵字subparttition)

RANGE分區或者LIST分區可以再次分割形成子分區,子分區可以是HASH分區或者KEY分區,例如:

create table users_par(
    id int not null,
    usrName varchar(50) not null,
    usrEmail varchar(50) not null,
    age int not null,
    birth date not null
)engine=InnoDB default charset=utf8
partition by range(age)
subpartition by hash(age)
subpartitions 2(
    partition p0 values less than(20),
    partition p1 values less than(30),
    partition p2 values less than maxvalue
);

10.分區時NULL值的處理

一般情況下,MYSQL把NULL當成零值后者最小值來處理。

在RANGE分區中,NULL會被當成最小值處理;LIST分區中,NULL值必須出現在枚舉值列表中,否則報錯;HASH/KEY分區中,NULL值被當成零值處理。

mysql> insert into student values (4010404,'zhumuxian','A1114',20,'');
Query OK, 1 row affected (0.04 sec)

mysql> insert into student values (4010405,'zhongzhaoxi','A1114',20,NULL);
ERROR 1526 (HY000): Table has no partition for value from column_list

mysql> select  PARTITION_NAME,PARTITION_EXPRESSION,PARTITION_DESCRIPTION,TABLE_ROWS from information_schema.partitions where table_schema=schema() and table_name='student';
+----------------+----------------------+-----------------------+------------+
| PARTITION_NAME | PARTITION_EXPRESSION | PARTITION_DESCRIPTION | TABLE_ROWS |
+----------------+----------------------+-----------------------+------------+
| p0             | `sgender`            | ''                  |          1 |
| p1             | `sgender`            | ''                  |          0 |
+----------------+----------------------+-----------------------+------------+
2 rows in set (0.01 sec)

11.分區管理

RANGE和LIST分區在刪除,添加,重新定義等分區管理上非常類似,如下所示。

刪除分區(alter table tbl_name drop partition partition_name),分區被刪除后,該分區的數據一起被刪除。

mysql> alter table users_par drop partition p0;
Query OK, 0 rows affected (0.24 sec)
Records: 0  Duplicates: 0  Warnings: 0

添加分區(alter table tbl_name add partition)

mysql>  alter table users_par add partition (partition p0 values less than (20));
ERROR 1481 (HY000): MAXVALUE can only be used in last partition definition
--這里報錯是因為添加分區必須在原分區的最大端添加,在為LIST分區添加分區時,新分區的值列表的值不能包含任意一個現有分區中值列表中的值,否則報錯
mysql> alter table student add partition (partition p2 values in (''));
ERROR 1495 (HY000): Multiple definition of same constant in list partitioning

重新定義分區(alter table tbl_name reorganize partition partition_name into),可以將一個分區拆開成多個,反之可以合並多個成一個或多個。

mysql> alter table users_par reorganize partition p1 into (partition p0 values less than (20),partition p1 values less t
han(30));
Query OK, 1 row affected (0.05 sec)
Records: 1  Duplicates: 0  Warnings: 0

需要注意的是:RANGE和LIST分區在重新定義時,只能重新定義相鄰的分區,不可以跳過分區,並且重新定義的分區區間必須和原分區區間一致,也不可以改變分區的類型。

HASH和KEY分區的管理

減少分區數量,使用coaleace關鍵字

mysql> alter table hash_par coalesce partition 2;
Query OK, 1 row affected (0.04 sec)
Records: 1  Duplicates: 0  Warnings: 0

增加分區數量

mysql> alter table hash_par add partition partitions 2;
Query OK, 1 row affected (0.04 sec)
Records: 1  Duplicates: 0  Warnings: 0

MySQL分區有利於查詢優化,快速刪除過期數據,提高查詢吞吐量等。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM