分区是按照一定规则把一个表分解成多个更小的表,更容易管理的部分,当访问数据库应用而言,逻辑上是一个表或一个索引,实际上是可以有数个物理对象组成,每个分区都是一个独立的对象,可以独自处理作为表的一部分进行数据处理
RANGE分区:基于一个给定连续区间分为,把数据分配到不通风分区
LIST :和RANGE类似,是基于给定枚举值,把数据分配到不同的分区
HASH:基于分区的个数,把数据分配到不同的分区
KEY:和HASH类似
无论哪种分区类型,要么分区表不包含主键或唯一键,要么分区表的主键或唯一键包含分区键,并且分区的名称是区分大小写的
按照range分区表示利用取值范围将数据分成分区,区间要连续且不能重叠,使用values less than 进行分区定义,如下
mysql> create table emp(
-> id int not null,
-> name varchar(10),
-> store_id int not null
-> )
-> partition by range (store_id)(
-> partition p0 values less than (10),
-> partition p1 values less than (20),
-> partition p2 values less than (30)
-> );
Query OK, 0 rows affected (0.07 sec)
mysql> insert into emp values (1,'jiepi',1),(2,'jiepi2',2),(3,'jiepi3',3);
Query OK, 3 rows affected (0.01 sec)
Records: 3 Duplicates: 0 Warnings: 0
我们把1-9存储到p0分区,10-19存储到p1,一次类推,但是当store_id大于30的时候会发生什么呢?
mysql> insert into emp values (50,'jiepi50',50);
ERROR 1526 (HY000): Table has no partition for value 50
我们发现他是报错的,因此我可以使用values less than maxvalue语句添加分区,maxvalue 代表的是最大的可能的整数值,当服务器不知道把数据放到哪个分区的时候,我们就把这个数据放到这个分区。
mysql> alter table emp add partition (partition p3 values less than maxvalue);
Query OK, 0 rows affected (0.08 sec)
Records: 0 Duplicates: 0 Warnings: 0
mysql> insert into emp values (50,'jiepi50',50);
Query OK, 1 row affected (0.01 sec)
list分区建立在离散的值列表告诉数据库应该放到个分区,list分区很多方面是和range分区相似,区别在于list分区从属于一个枚举列表的值的集合,range是一个连续区间的集合,
list分区使用 partition by list(expr) 实现,expr 是某列值,或一个基于某列值得表达式,然后通过 values in (value_list) 方式定义分区,始终value_list是用逗号分隔的整数列表,他也不必按照上面顺序声明。
mysql> create table expenses(
-> id int not null,
-> category int,
-> amout decimal (10,3)
-> )partition by list (category) (
-> partition p0 values in (3,5),
-> partition p1 values in (1,10),
-> partition p2 values in (4,9),
-> partition p3 values in (2),
-> partition p4 values in (6)
-> );
Query OK, 0 rows affected (0.15 sec)
mysql> insert into expenses values (1,1,12.9),(2,2,12.8);
Query OK, 2 rows affected (0.01 sec)
Records: 2 Duplicates: 0 Warnings: 0
注意的是list分区没有像range分区有values less than maxvalue,如果数据在list分区中不到会报错,所以定分区的时候必须包含多有可能的值。
mysql> insert into expenses values (1,11,12.9),(2,2,12.8);
ERROR 1526 (HY000): Table has no partition for value 11
Columns分区是在mysql5.5引进的分区类型,上面的分区是都是基于整形分区,是为了解决之前版本要进行函数或者表达式转换成整形,他分为 list columns 和 range columns ,他们支持 整形 日期 ,字符串,
整形:tinyint smallint ,mediumint ,int ,bigint ,其他类型不支持
日期:data ,datatime
字符串:char ,varcahr ,binary ,varbinary 不支持 text和blob 类型做分区键
除了添加了类型支持,并且还支持多列分区.
mysql> CREATE TABLE m_num(
-> 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,MAXVALUE),
-> PARTITION p3 VALUES LESS THAN (MAXVALUE,MAXVALUE)
-> );
Query OK, 0 rows affected (0.16 sec)
他的分区规则优点稍微不一样,他是按照字段组的比较
mysql> insert into m_num values (1,10);
Query OK, 1 row affected (0.01 sec)
mysql> select (1,10)<(10,10) from m_num;
+----------------+
| (1,10)<(10,10) |
+----------------+
| 1 |
+----------------+
1 row in set (0.00 sec)
mysql> select
-> partition_name part ,
-> partition_expression expr,
-> partition_description descr,
-> table_rows
-> from
-> information_schema.partitions
-> where table_schema=schema()
-> and table_name='m_num';
+------+---------+-------------------+------------+
| part | expr | descr | table_rows |
+------+---------+-------------------+------------+
| p0 | `a`,`b` | 0,10 | 0 |
| p1 | `a`,`b` | 10,20 | 1 |
| p2 | `a`,`b` | 10,MAXVALUE | 0 |
| p3 | `a`,`b` | MAXVALUE,MAXVALUE | 0 |
+------+---------+-------------------+------------+
4 rows in set (0.01 sec)
他的比较原则如下
我们在看一个例子
mysql> insert into m_num values (10,25);
Query OK, 1 row affected (0.01 sec)
mysql> select partition_name part , partition_expression expr, partition_description descr, table_rows from information_schema.partitions where table_schema=schema() and table_name='m_num';
+------+---------+-------------------+------------+
| part | expr | descr | table_rows |
+------+---------+-------------------+------------+
| p0 | `a`,`b` | 0,10 | 0 |
| p1 | `a`,`b` | 10,20 | 1 |
| p2 | `a`,`b` | 10,MAXVALUE | 1 |
| p3 | `a`,`b` | MAXVALUE,MAXVALUE | 0 |
+------+---------+-------------------+------------+
4 rows in set (0.00 sec)
hash分区主要用来分散热点读,确保数据在预先知道分区数目,尽可能的平均分布,在数据进行分区的时候,使用一个散列函数,计算数据到那个分区.
hash分区分为两类 常规hash和线性hash分区,常规分区是使用模运算计算,而线性hash是一个线性的2的幂运算规则。
我们使用 partition by hash (expr) partitions num实现
mysql> create table emp_hash(
-> id int not null,
-> name varchar(20),
-> store_id int not null
-> )
-> partition by hash (store_id) partitions 4;
Query OK, 0 rows affected (0.14 sec)
hash分区的数据是按照N=MOD(expr,num)计算的,比如我插入一个store_id=234的数据,那么他存储的数据计算在N=MOD(234,4)=2 分区。
mysql> insert into emp_hash values (1,'jiepi',234);
Query OK, 1 row affected (0.00 sec)
mysql> explain partitions select * from emp_hash where store_id=234 \G;
*************************** 1. row ***************************
id: 1
select_type: SIMPLE
table: emp_hash
partitions: p2
type: ALL
possible_keys: NULL
key: NULL
key_len: NULL
ref: NULL
rows: 2
Extra: Using where
1 row in set (0.00 sec)
expr可以是一个表达式也可以是一某列的值,当进行插入删除更新操作的时候,这个表达式都要重新计算一次,所以在表达式比较复杂的时候,还是很消耗性能的,建议不要使用这种分区方式。
Hash分区在增加分区也是一个比较麻烦的事情,因为要把以前的数据重新计算分配到新的分区的需求,因此我们还有一种线性Hash分区,分区函数是一个线性的2的幂的运算规则。比常规hash分区多了一个linear.
mysql> create table emp_hash_linear(
-> id int not null,
-> name varchar(20),
-> store_id int not null
-> )
-> partition by linear hash (store_id) partitions 4;
他的就算方式如下公式
mysql> insert into emp_hash_linear values (1,'jiepi',234);
Query OK, 1 row affected (0.01 sec)
mysql> explain partitions select * from emp_hash_linear where store_id=234 \G;
*************************** 1. row ***************************
id: 1
select_type: SIMPLE
table: emp_hash_linear
partitions: p2
type: ALL
possible_keys: NULL
key: NULL
key_len: NULL
ref: NULL
rows: 2
Extra: Using where
1 row in set (0.00 sec)
KEY分区和HASH分区类似,使用hash进行分区,只不过Hash分区支持自定义表达式,而key不支持,使用的是mysql服务器提供的HASH函数,同时hash只支持整数分区,而key分区除了Text和BLOB其他类型都支持,
mysql> create table emp_key(
-> id int not null,
-> name varchar(20),
-> job varchar(20)
-> )
-> partition by key (job) partitions 4
-> ;
Query OK, 0 rows affected (0.11 sec)
mysql> create table emp_key_primary(
-> id int not null,
-> name varchar(20),
-> job varchar(20),
-> primary key (id)
-> )
-> partition by key () partitions 4;
Query OK, 0 rows affected (0.10 sec)
mysql> create table emp_key_unique(
-> id int not null,
-> name varchar(20),
-> job varchar(20),
-> unique key (id)
-> )
-> partition by key () partitions 4;
Query OK, 0 rows affected (0.11 sec)
需要注意的是,我们可以不指定分区间,默认会选取主键,其次是唯一键作为分区间,如果没有主键和唯一键,就不能不指定分区键了。key分区也是使用线性2的幂计算出数据在哪个分区。当我们处理大量记录时,能够有效的分散热点。
mysql不禁止在分区键上使用null,mysql是把null值按照最小值,或者零值进行处理,range分区是按照最小值处理,list分区中,null值必须出现在枚举中,否则不被接受,Hash和key分区,把null按照零值处理。
添加,删除,重新定义分区处理上,range和list 语法基本一直,我们来来看一下
先创建range分区,再插入数据,查看数据在p2,在使用
alter table range_test drop partition p2;
mysql> create table range_test(
-> id int not null,
-> separated date not null default '9999-12-31',
-> store_id int not null
-> )
-> partition by range(year(separated))(
-> partition p0 values less than (1995),
-> partition p1 values less than (2000),
-> partition p2 values less than (2005),
-> partition p3 values less than (2015)
-> );
Query OK, 0 rows affected (0.10 sec)
mysql> insert into range_test values (1,'2002-12-01',1);
Query OK, 1 row affected (0.00 sec)
mysql> select partition_name part ,
-> partition_expression expr,
-> partition_description descr,
-> table_rows from
-> information_schema.partitions
-> where table_schema=schema()
-> and table_name='range_test';
+------+-----------------+-------+------------+
| part | expr | descr | table_rows |
+------+-----------------+-------+------------+
| p0 | year(separated) | 1995 | 0 |
| p1 | year(separated) | 2000 | 0 |
| p2 | year(separated) | 2005 | 1 |
| p3 | year(separated) | 2015 | 0 |
+------+-----------------+-------+------------+
4 rows in set (0.00 sec)
mysql> alter table range_test drop partition p2;
Query OK, 0 rows affected (0.05 sec)
Records: 0 Duplicates: 0 Warnings: 0
mysql> select partition_name part ,
-> partition_expression expr,
-> partition_description descr,
-> table_rows from
-> information_schema.partitions
-> where table_schema=schema()
-> and table_name='range_test';
+------+-----------------+-------+------------+
| part | expr | descr | table_rows |
+------+-----------------+-------+------------+
| p0 | year(separated) | 1995 | 0 |
| p1 | year(separated) | 2000 | 0 |
| p3 | year(separated) | 2015 | 0 |
+------+-----------------+-------+------------+
3 rows in set (0.00 sec)
2.range添加分区
注意的是range分区只能在最大端增加分区,否则会报错
mysql> alter table range_test add partition ( partition p4 values less than (2030));
Query OK, 0 rows affected (0.07 sec)
Records: 0 Duplicates: 0 Warnings: 0
mysql> show create table range_test\G
*************************** 1. row ***************************
Table: range_test
Create Table: CREATE TABLE `range_test` (
`id` int(11) NOT NULL,
`separated` date NOT NULL DEFAULT '9999-12-31',
`store_id` int(11) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=latin1
/*!50100 PARTITION BY RANGE (year(separated))
(PARTITION p0 VALUES LESS THAN (1995) ENGINE = InnoDB,
PARTITION p1 VALUES LESS THAN (2000) ENGINE = InnoDB,
PARTITION p3 VALUES LESS THAN (2015) ENGINE = InnoDB,
PARTITION p4 VALUES LESS THAN (2030) ENGINE = InnoDB) */
1 row in set (0.00 sec)
mysql> alter table range_test add partition ( partition p5 values less than (2025));
ERROR 1493 (HY000): VALUES LESS THAN value must be strictly increasing for each partition
3.range重新定义分区
mysql> show create table range_test\G;
*************************** 1. row ***************************
Table: range_test
Create Table: CREATE TABLE `range_test` (
`id` int(11) NOT NULL,
`separated` date NOT NULL DEFAULT '9999-12-31',
`store_id` int(11) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=latin1
/*!50100 PARTITION BY RANGE (year(separated))
(PARTITION p0 VALUES LESS THAN (1995) ENGINE = InnoDB,
PARTITION p1 VALUES LESS THAN (2000) ENGINE = InnoDB,
PARTITION p3 VALUES LESS THAN (2015) ENGINE = InnoDB,
PARTITION p4 VALUES LESS THAN (2030) ENGINE = InnoDB) */
1 row in set (0.00 sec)
ERROR:
No query specified
mysql> alter table range_test reorganize partition p3 into(
-> partition p2 values less than (2005),
-> partition p3 values less than (2015));
Query OK, 0 rows affected (0.14 sec)
Records: 0 Duplicates: 0 Warnings: 0
mysql> show create table range_test\G
*************************** 1. row ***************************
Table: range_test
Create Table: CREATE TABLE `range_test` (
`id` int(11) NOT NULL,
`separated` date NOT NULL DEFAULT '9999-12-31',
`store_id` int(11) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=latin1
/*!50100 PARTITION BY RANGE (year(separated))
(PARTITION p0 VALUES LESS THAN (1995) ENGINE = InnoDB,
PARTITION p1 VALUES LESS THAN (2000) ENGINE = InnoDB,
PARTITION p2 VALUES LESS THAN (2005) ENGINE = InnoDB,
PARTITION p3 VALUES LESS THAN (2015) ENGINE = InnoDB,
PARTITION p4 VALUES LESS THAN (2030) ENGINE = InnoDB) */
1 row in set (0.00 sec)
4.list分区重新定义分区
mysql> show create table list_test\G
*************************** 1. row ***************************
Table: list_test
Create Table: CREATE TABLE `list_test` (
`id` int(11) NOT NULL,
`separated` date NOT NULL DEFAULT '9999-12-31',
`store_id` int(11) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=latin1
/*!50100 PARTITION BY LIST (store_id)
(PARTITION p0 VALUES IN (3,5) ENGINE = InnoDB,
PARTITION p1 VALUES IN (1,2) ENGINE = InnoDB,
PARTITION p2 VALUES IN (4,7) ENGINE = InnoDB,
PARTITION p3 VALUES IN (6) ENGINE = InnoDB) */
1 row in set (0.00 sec)
mysql> alter table list_test add partition (partition p4 values in (8));
Query OK, 0 rows affected (0.06 sec)
Records: 0 Duplicates: 0 Warnings: 0
mysql> show create table list_test \G
*************************** 1. row ***************************
Table: list_test
Create Table: CREATE TABLE `list_test` (
`id` int(11) NOT NULL,
`separated` date NOT NULL DEFAULT '9999-12-31',
`store_id` int(11) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=latin1
/*!50100 PARTITION BY LIST (store_id)
(PARTITION p0 VALUES IN (3,5) ENGINE = InnoDB,
PARTITION p1 VALUES IN (1,2) ENGINE = InnoDB,
PARTITION p2 VALUES IN (4,7) ENGINE = InnoDB,
PARTITION p3 VALUES IN (6) ENGINE = InnoDB,
PARTITION p4 VALUES IN (8) ENGINE = InnoDB) */
1 row in set (0.00 sec)
mysql> alter table list_test reorganize partition p2 ,p3,p4 into (
-> partition p2 values in (4),
-> partition p3 values in (6),
-> partition p4 values in (7,8));
Query OK, 0 rows affected (0.19 sec)
Records: 0 Duplicates: 0 Warnings: 0
mysql> show create table list_test\G
*************************** 1. row ***************************
Table: list_test
Create Table: CREATE TABLE `list_test` (
`id` int(11) NOT NULL,
`separated` date NOT NULL DEFAULT '9999-12-31',
`store_id` int(11) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=latin1
/*!50100 PARTITION BY LIST (store_id)
(PARTITION p0 VALUES IN (3,5) ENGINE = InnoDB,
PARTITION p1 VALUES IN (1,2) ENGINE = InnoDB,
PARTITION p2 VALUES IN (4) ENGINE = InnoDB,
PARTITION p3 VALUES IN (6) ENGINE = InnoDB,
PARTITION p4 VALUES IN (7,8) ENGINE = InnoDB) */
1 row in set (0.01 sec)
HASH和KEY分区管理
1.减少Hash分区
mysql> create table hash_test(
-> id int not null,
-> name varchar(10),
-> store_id int not null
-> )
-> partition by hash(store_id) partitions 4;
Query OK, 0 rows affected (0.09 sec)
mysql> alter table hash_test coalesce partition 2;
Query OK, 0 rows affected (0.16 sec)
Records: 0 Duplicates: 0 Warnings: 0
mysql> show create table hash_test \G
*************************** 1. row ***************************
Table: hash_test
Create Table: CREATE TABLE `hash_test` (
`id` int(11) NOT NULL,
`name` varchar(10) DEFAULT NULL,
`store_id` int(11) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=latin1
/*!50100 PARTITION BY HASH (store_id)
PARTITIONS 2 */
1 row in set (0.00 sec)
2.增加hash分区
mysql> alter table hash_test add partition partitions 8;
Query OK, 0 rows affected (0.32 sec)
Records: 0 Duplicates: 0 Warnings: 0
mysql> show create table hash_test\G
*************************** 1. row ***************************
Table: hash_test
Create Table: CREATE TABLE `hash_test` (
`id` int(11) NOT NULL,
`name` varchar(10) DEFAULT NULL,
`store_id` int(11) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=latin1
/*!50100 PARTITION BY HASH (store_id)
PARTITIONS 10 */
1 row in set (0.00 sec)
注意alter table add partition partitions n 新增hash分区或key分区是在原表上再添加n个分区,不是增加到n个分区。