分区原理
分区表是由多个相关的底层表实现,这些底层表也是由句柄对象表示,所以我们也可以直接访问各个分区,存储引擎管理分区和一个普通表没有任何不同,存储引擎也无须知道这是一个普通表是分区表的一部分。
在分区表上的操作是按照下面的操作逻辑进行:
select 查询:
当查询一份分区表的时候,分区层先打开并锁住所有的 底层表,优化器判断是否可以过滤部分分区,然后再调 用对应的存储接口访问各个分区。
insert操作
当写入一条记录时,分区层打开并锁住所有底层表,然后确定那个分区接受这条记录,再将这条记录写入对应的底层表
delete操作
当删除一条记录是,分区层先打开并锁住所有底层表,mysql先确定对象分区,最后对相应的底层表进行删除操作。
update操作
当更新一条数据时,分区层先打开并锁住所有的底层表,mysql先确定更新的记录再哪个分区,然后取出数据并更新,在判断更新后的数据应该放在哪个分区,然后对底层表进行写入操作,并对原数据的底层表进行删除操作
分区使用的场景
A : 表非常大以至于无法全部都放在内存中,或者只在表的最后部分有热点数据,其他都是历史数据
B:分区表的数据容易维护,如:想批量删除数据可以清楚整个分区的方式,另外,还可以对一个独立分区进行优化、检查、修复等操作。
C : 分区的数据可以分布在不同物理设备上,从而高效地利用多个硬件设备。
D: 可以使用分区表来避免某些特殊的瓶颈
E:如果需要,还可以备份和恢复独立的分页去,这在非常大的数据集场景下效果非常有效
F:优化查询,在where字句中包含分区列时,可以只使用必要的分区来提高查询效率,同时在涉及sum()和count()这类聚合函数的查询时,可以在每个分区上面并行处理,最终只需要汇总所有分区得到的结果。
A:一个表最多只能有1024个分区(mysql5.6之后支持8192个分区)
B:在mysql5.1中分区表达式必须是整数,或者返回整数表达式,在5.5之后,某些场景可以直接使用字符串和日期类型列进行分区
C:如果分区字段中有主键或者唯一索引列,那么所有主键列和唯一索引列都必须包含进来,如果表中有主键或唯一索引,那么分区键必须是主键或唯一索引。
D:分区表中无法使用外键约束
E:mysql数据库支持的分区类型为水平分区,并不支 持垂直分区,因此,mysql数据库的分区中索引是局部分区索引,一个分区中既存放了数据又存放了索引,而全局分区是指的数据库放在各个分区中,但是所有的数据的索引放在另外一个对象中
F:目前mysql不支持空间类型和临时表类型进行分区。不支持全文索引
分区类型 | 说明 | 使用频率 |
---|---|---|
range | 对连续的值进行分区,例如日期,年月 | 较高 |
list | 基于列值匹配一个离散值集合中的某个值来进行选择 | 一般 |
hash | 分散热点读取,确保数据在预定确定个数分区中尽可能的平均分布 | 较多 |
key | 类似于hash分区,区别在于KEY分区支持只支持计算一列或多列,且MySQL服务器提供自身的哈希函数。必须有一列或多列包含整数值 | 一般 |
CREATE TABLE IF NOT EXISTS ruanl_user (
id INT(11) NOT NULL AUTO_INCREMENT COMMENT '用户id',
user_name VARCHAR(50) NOT NULL COMMENT '用户姓名',
sex INT(2) NOT NULL COMMENT '性别 0为男 1为女',
PRIMARY KEY(id)
)ENGINE INNODB DEFAULT CHARSET = utf8 AUTO_INCREMENT = 1
PARTITION BY RANGE (id) (
PARTITION p0 VALUES LESS THAN (3),
PARTITION p1 VALUES LESS THAN (6),
PARTITION p2 VALUES LESS THAN (9),
PARTITION p3 VALUES LESS THAN (12),
PARTITION p4 VALUES LESS THAN maxvalue
);
通过查看下面语句来查看分区分布
SELECT TABLE_NAME, PARTITION_NAME, TABLE_ROWS, AVG_ROW_LENGTH, DATA_LENGTH
FROM INFORMATION_SCHEMA.PARTITIONS
WHERE TABLE_NAME LIKE 'ruanl_user'
CREATE TABLE employees (
id INT NOT NULL,
first_name VARCHAR(30),
last_name VARCHAR(30),
store_id INT NOT NULL,
create_time DATE NOT NULL DEFAULT '9999-12-31',
)
PARTITION BY LIST(store_id)
PARTITION pNorth VALUES IN (1,5,6,9,17),
PARTITION pEast VALUES IN (2,4,10,11,19,20),
PARTITION pWest VALUES IN (3,12,13,14,18),
PARTITION pCentral VALUES IN (7,8,15,16)
)
*如果插入一行值时,里面的store_id不在上面的这列value_list时会出现插入失败并报错。而且。List分区没有range分区那样,range分区的“VALUES LESS THAN MAXVALUE”会将其他的值包含在内的定义*.
hash分区主要用来分散热点读取,确保数据在预定确定个数分区中尽可能的平均分布。一个表执行hash分区,mysql会对分区键应用一个散列函数,以此确定数据应该放在n个分区中的哪一个分区.
常规hash分区
CREATE TABLE employees (
id INT NOT NULL,
first_name VARCHAR(30),
last_name VARCHAR(30),
store_id INT NOT NULL,
create_time DATE NOT NULL DEFAULT '9999-12-31',
)
PARTITION BY HASH(id)
PARTITIONS 5;
CREATE TABLE employees (
id INT NOT NULL,
first_name VARCHAR(30),
last_name VARCHAR(30),
store_id INT NOT NULL,
create_time DATE NOT NULL DEFAULT '9999-12-31',
)
PARTITION BY LINEAR HASH(id)
PARTITIONS 5
key分区和hash分区的区别:
支持类型
整形支持:tinyint,smallint,mediumint,int,bigint;不支持decimal和float
时间类型支持:date,datetime
字符类型支持:char,varchar,binary,varbinary;不支持text,blob
单字段分区
alter table employees partition by range(column(create_time))
(PARTITION p201701 VALUES LESS THAN ('2017-02-01')) ENGINE = InnoDB,
PARTITION p201702 VALUES LESS THAN ('2017-02-01') ENGINE = InnoDB,
PARTITION p201703 VALUES LESS THAN ('2017-02-01') ENGINE = InnoDB);
多字段分区
alter table employees partition by range(column(store_id,create_time))
(PARTITION p201701 VALUES LESS THAN (10,'2017-02-01')) ENGINE = InnoDB,
PARTITION p201702 VALUES LESS THAN (20,'2017-02-01') ENGINE = InnoDB,
PARTITION p201703 VALUES LESS THAN (30,'2017-02-01') ENGINE = InnoDB);
多字段的分区键比较是基于数组的比较。它先用插入的数据的第一个字段值和分区的第一个值进行比较,如果插入的第一个值小于分区的第一个值那么就不需要比较第二个值就属于该分区;如果第一个值等于分区的第一个值,开始比较第二个值同样如果第二个值小于分区的第二个值那么就属于该分区
alter table employees partition by list(column(create_time))
(PARTITION p201701 VALUES LESS THAN ('2017-02-01','2017-03-01') ENGINE = InnoDB,
PARTITION p201702 VALUES LESS THAN ('2017-04-01') ENGINE = InnoDB,
PARTITION p201703 VALUES LESS THAN ('2017-05-01') ENGINE = InnoDB);
alter table employees partition by list(column(id,create_time))
(PARTITION p201701 VALUES LESS THAN ((1,'2017-02-01'),(1,'2017-03-01')) ENGINE = InnoDB,
PARTITION p201702 VALUES LESS THAN (2,'2017-04-01') ENGINE = InnoDB,
PARTITION p201703 VALUES LESS THAN (3,'2017-05-01') ENGINE = InnoDB);
## 分区操作
CREATE TABLE employees (
id INT NOT NULL,
first_name VARCHAR(30),
last_name VARCHAR(30),
store_id INT NOT NULL,
create_time DATE NOT NULL DEFAULT '9999-12-31',
)
partition BY
range(to_days(create_time))
(PARTITION p201701 VALUES LESS THAN (TO_DAYS('2017-02-01')) ENGINE = InnoDB,
PARTITION p201702 VALUES LESS THAN (TO_DAYS('2017-03-01')) ENGINE = InnoDB,
PARTITION p201703 VALUES LESS THAN (TO_DAYS('2017-04-01')) ENGINE = InnoDB);
alter table employees partition by range(to_days(create_time))
(PARTITION p201701 VALUES LESS THAN (TO_DAYS('2017-02-01')) ENGINE = InnoDB,
PARTITION p201702 VALUES LESS THAN (TO_DAYS('2017-03-01')) ENGINE = InnoDB,
PARTITION p201703 VALUES LESS THAN (TO_DAYS('2017-04-01')) ENGINE = InnoDB);
alter table employees add partition(PARTITION p2018 VALUES LESS THAN MAXVALUE);
只能从range分区列表最大端增加分区
增加list分区,不能添加一个包含现有分区值列表中的任意值分区,也就是说对一个固定的分区键值,必须指定并且只能指定一个 唯一的分区
alter table employees drop partition p201702;
这种删除会是删除表中的数据
alter table employees remove partitioning
使用remove移除分区是仅仅移除分区的定义,并不会删除数据和drop PARTITION不一样,后者会连同数据一起删除
ALTER TABLE employees REORGANIZE PARTITION p201701,p201702,p201703,p2018 INTO (PARTITION p0 VALUES LESS THAN MAXVALUE);
将p201701,p201702,p201703,p2018这几个分区数据移到p0分区
注意 hash 和 key 分区不能使用 REORGANIZE
## 日期函数分区方法
alter table employees partition by range(YEAR(create_time))
(PARTITION p201701 VALUES LESS THAN ('2015-02-01') ENGINE = InnoDB,
PARTITION p201702 VALUES LESS THAN ('2016-02-01') ENGINE = InnoDB,
PARTITION p201703 VALUES LESS THAN ('2017-02-01') ENGINE = InnoDB);
对于日期字段分区,查询条件使用> 、< 、betnwen、=都会利用分区查询,如果条件使用函数转换则不会走分区,比如使用YEAR()。
alter table employees partition by range(UNIX_TIMESTAMP(create_time))
(PARTITION p201701 VALUES LESS THAN (UNIX_TIMESTAMP('2015-02-01')) ENGINE = InnoDB,
PARTITION p201702 VALUES LESS THAN (UNIX_TIMESTAMP('2016-02-01')) ENGINE = InnoDB,
PARTITION p201703 VALUES LESS THAN (UNIX_TIMESTAMP('2017-02-01')) ENGINE = InnoDB);
当往分区列中插入null值RANG 分区会将其当作最小值来处理即插入最小的分区中