现在的位置: 首页 > 数据库开发经验 > 正文

MySQL分区表–列范围分区

2016年11月10日 数据库开发经验 ⁄ 共 6196字 ⁄ 字号 评论关闭

MySQL分区表–列范围分区

列范围分区与范围分区类似,但是允许基于多个列值的分区。另外,列范围分区不要求列的类型必须是int型的。

列范围分区与范围分区有几点不同:

  • 列范围分区不接受表达式,只接受列名。
  • 列范围分区接受一个或多个列的名字列表。
    列范围分区基于列值列表中数据的比较结果,而不是与标量值得比较结果。数据存放也是基于列值列表数据的比较结果。
  • 列范围分区使用的列不限制类型为整型。string, Date或者Datetime都可以用作分区。

列范围分区的基本语法如下:

CREATE TABLE table_name
PARTITIONED BY RANGE COLUMNS(column_list) (
PARTITION partition_name VALUES LESS THAN (value_list)[,
PARTITION partition_name VALUES LESS THAN (value_list)][,...]
)

column_list:
column_name[, column_name][, ...]

value_list:
value[, value][, ...]

语法中的column_list是一个包含一个或者多个列明的列表(有时叫分区列表),value_list是值的列表(分区定义值的列表)。每个分区都必须有一个value_list,value_list中value的数量必须与column_list中列的数量相等。一般来说,使用n个列来创建列范围分区表,那每个VALUE LESS THAN使用的值列表中必须有n个值。
分区列表和值列表中的元素必须以相同的顺序出现。值列表中的元素必须对应分区列表中列的类型。但是分区列表中列的顺序不需要跟创建table语句中列的顺序相同。使用RANGE进行分区,还可以使用MAXVALUE来代表一个大于任何一个合法的值的数。下面是一个列分区范围的例子:

CREATE TABLE rcx (
a INT,
b INT,
c CHAR(3),
d INT
)
PARTITION BY RANGE COLUMNS(a,d,c) (
PARTITION p0 VALUES LESS THAN (5,10,'ggg'),
PARTITION p1 VALUES LESS THAN (10,20,'mmmm'),
PARTITION p2 VALUES LESS THAN (15,30,'sss'),
PARTITION p3 VALUES LESS THAN (MAXVALUE,MAXVALUE,MAXVALUE)
);

rcx中有a,b,c,d四列,使用a,b,c三个列进行分区。每个值列表中以相同顺序定义3个值;每个值列表的形式为(INT, INT, CHAR(3)), 代表着a,b,c三个列的类型。
往分区进行插入时,会对插入的值和分区定义的值列表进行比较,然后决定插入的分区。因为我们比较的是值得列表,而不是标量值,所以RANGE COLUMNS与简单的RANGE使用的VALUES LESS THAN从语义上有一些不同。在RANGE分区中,表达式作用在每一行上返回的结果如果等于VALUES LESS THAN的限定值,是不会插入到相应的分区中的;但是使用RANGE COLUMNS,有时一行数据的分区列表的第一个元素等于VALUES LESS THAN的值列表的第一个元素是会被插入到相应的分区。
考虑下面的分区表:

CREATE TABLE r1 (
a INT,
b INT
)
PARTITION BY RANGE (a) (
PARTITION p0 VALUES LESS THAN (5),
PARTITION p1 VALUES LESS THAN (MAXVALUE)
);

假设我们插入三条数据,每条数据a列的值都是5,3条数据都会插入到分区p1中,因为每条数据a列的值都不小于5:

mysql> INSERT INTO r1 VALUES (5,10), (5,11), (5,12);
Query OK, 3 rows affected (0.00 sec)
Records: 3 Duplicates: 0 Warnings: 0

mysql> SELECT PARTITION_NAME,TABLE_ROWS
-> FROM INFORMATION_SCHEMA.PARTITIONS
-> WHERE TABLE_NAME = 'r1';
+----------------+------------+
| PARTITION_NAME | TABLE_ROWS |
+----------------+------------+
| p0 | 0 |
| p1 | 3 |
+----------------+------------+
2 rows in set (0.00 sec)

现在我们使用a,b来对r1进行RANGE COLUMNS分区:

CREATE TABLE rc1 (
a INT,
b INT
)
PARTITION BY RANGE COLUMNS(a, b) (
PARTITION p0 VALUES LESS THAN (5, 12),
PARTITION p1 VALUES LESS THAN (MAXVALUE, MAXVALUE)
);

我们往这张表里插入相同的三条数据:

mysql> INSERT INTO rc1 VALUES (5,10), (5,11), (5,12);
Query OK, 3 rows affected (0.00 sec)
Records: 3 Duplicates: 0 Warnings: 0

mysql> SELECT PARTITION_NAME,TABLE_ROWS
-> FROM INFORMATION_SCHEMA.PARTITIONS
-> WHERE TABLE_NAME = 'rc1';
+--------------+----------------+------------+
| TABLE_SCHEMA | PARTITION_NAME | TABLE_ROWS |
+--------------+----------------+------------+
| p | p0 | 2 |
| p | p1 | 1 |
+--------------+----------------+------------+
2 rows in set (0.00 sec)

这是因为数据插入时比较的是值列表,而不是标量值:

mysql> SELECT (5,10) < (5,12), (5,11) < (5,12), (5,12) < (5,12);
+-----------------+-----------------+-----------------+
| (5,10) < (5,12) | (5,11) < (5,12) | (5,12) < (5,12) |
+-----------------+-----------------+-----------------+
| 1 | 1 | 0 |
+-----------------+-----------------+-----------------+
1 row in set (0.00 sec)

MySQL认为(5,10)和(5,11)两个列表小于(5,12),所以这两个列表存放在分区p0中。因为5不小于5,12不小于12, 所以(5,12)不小于(5,12),所以存在分区p1中。

如果对一张表的一个列进行RANGE COLUMNS分区,数据分布的分区与使用RANGE分区一样。
使用RANGE COLUMNS分区,只要值列表是严格意义上的增长的,那分区定义的语法就是合法的:

CREATE TABLE rc2 (
a INT,
b INT
)
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)
);

CREATE TABLE rc3 (
a INT,
b INT
)
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 (10,35),
PARTITION p4 VALUES LESS THAN (20,40),
PARTITION p5 VALUES LESS THAN (MAXVALUE,MAXVALUE)
);

下面的语句也可以执行成功。可能第一眼看上去这个语句会执行失败,因为分区p0的b列的限制值是25,分区p1的b列的值是20;p1的c列的值是100, p2的c列的值是50:

CREATE TABLE rc4 (
a INT,
b INT,
c INT
)
PARTITION BY RANGE COLUMNS(a,b,c) (
PARTITION p0 VALUES LESS THAN (0,25,50),
PARTITION p1 VALUES LESS THAN (10,20,100),
PARTITION p2 VALUES LESS THAN (10,30,50)
PARTITION p3 VALUES LESS THAN (MAXVALUE,MAXVALUE,MAXVALUE)
);

当设计RANGE COLUMNS分区表示,可以是下面的语句来测试分区定义是不是正确的:

mysql> SELECT (0,25,50) < (10,20,100), (10,20,100) < (10,30,50);
+-------------------------+--------------------------+
| (0,25,50) < (10,20,100) | (10,20,100) < (10,30,50) |
+-------------------------+--------------------------+
| 1 | 1 |
+-------------------------+--------------------------+
1 row in set (0.00 sec)

当CREATE TABLE语句包含的分区定义不是严格增长的顺序,创建语句会失败:

mysql> CREATE TABLE rcf (
-> a INT,
-> b INT,
-> c INT
-> )
-> PARTITION BY RANGE COLUMNS(a,b,c) (
-> PARTITION p0 VALUES LESS THAN (0,25,50),
-> PARTITION p1 VALUES LESS THAN (20,20,100),
-> PARTITION p2 VALUES LESS THAN (10,30,50),
-> PARTITION p3 VALUES LESS THAN (MAXVALUE,MAXVALUE,MAXVALUE)
-> );
ERROR 1493 (HY000): VALUES LESS THAN value must be strictly increasing for each partition

我们看到这样的错误时,可以推断出是哪个分区定义是非法的。上面的案例中,p2的分区定义不小于p3的分区定义:

mysql> SELECT (0,25,50) < (20,20,100), (20,20,100) < (10,30,50);
+-------------------------+--------------------------+
| (0,25,50) < (20,20,100) | (20,20,100) < (10,30,50) |
+-------------------------+--------------------------+
| 1 | 0 |
+-------------------------+--------------------------+
1 row in set (0.00 sec)

使用RANGE COLUMNS分区时,MySQL允许在多个VALUES LESS THAN中使用MAXVALUE。然而,对单个列使用RANGE COLUMNS必须保证严格增长,不能对超过1个的VALUES LESS THAN使用MAXVALUE。另外,不能对第一个列的分区定义多次使用MAXVALUE。
上面我们提到过,RANGE COLUMNS没有限制分区的列必须是整型。下面我们有一个未分区的employees表:

CREATE TABLE employees (
id INT NOT NULL,
fname VARCHAR(30),
lname VARCHAR(30),
hired DATE NOT NULL DEFAULT '1970-01-01',
separated DATE NOT NULL DEFAULT '9999-12-31',
job_code INT NOT NULL,
store_id INT NOT NULL
);

我们使用RANGE COLUMNS将employees分成四个分区:

CREATE TABLE employees_by_lname (
id INT NOT NULL,
fname VARCHAR(30),
lname VARCHAR(30),
hired DATE NOT NULL DEFAULT '1970-01-01',
separated DATE NOT NULL DEFAULT '9999-12-31',
job_code INT NOT NULL,
store_id INT NOT NULL
)
PARTITION BY RANGE COLUMNS (lname) (
PARTITION p0 VALUES LESS THAN ('g'),
PARTITION p1 VALUES LESS THAN ('m'),
PARTITION p2 VALUES LESS THAN ('t'),
PARTITION p3 VALUES LESS THAN (MAXVALUE)
);

我们也可以使用ALTER TABLE语句来改造employees表:

ALTER TABLE employees PARTITION BY RANGE COLUMNS (lname) (
PARTITION p0 VALUES LESS THAN ('g'),
PARTITION p1 VALUES LESS THAN ('m'),
PARTITION p2 VALUES LESS THAN ('t'),
PARTITION p3 VALUES LESS THAN (MAXVALUE)
);

不同的字符集和校对,会对字符串排序产生不同的影响。使用字符串列进行分区,字符集和校对也会对一个行数据最终分布在哪个分区产生影响。另外,在表创建后对数据库、表、或者字段修改字符集和校对,也会对数据行的分布带来变化。

我们也可以对employees表进行修改,让该表以员工入职时间按每十年进行分区:

ALTER TABLE employees PARTITION BY RANGE COLUMNS (hired) (
PARTITION p0 VALUES LESS THAN ('1970-01-01'),
PARTITION p1 VALUES LESS THAN ('1980-01-01'),
PARTITION p2 VALUES LESS THAN ('1990-01-01'),
PARTITION p3 VALUES LESS THAN ('2000-01-01'),
PARTITION p4 VALUES LESS THAN ('2010-01-01'),
PARTITION p5 VALUES LESS THAN (MAXVALUE)
);

抱歉!评论已关闭.

×