官网地址:https://dev.mysql.com/doc/refman/8.0/en/partitioning.html
假设现在有提条业务线,需要用到100张表,每张表里面有100w条数据,我们现在需要遍历这些数据,从中取出一条符合条件的记录,最坏情况,可能需要遍历100*100w,这数据量是多么庞大。这时候我们就需要对表进行拆分了:
- 垂直拆分:可以按照业务线维护进行聚合,如按照订单表,用户表,商品表等多种业务维度进行聚合到一个DB
- 水平拆分:将每张表里面的100w条记录进行水平拆分,如第1-10w条记录放入第一个文件,第10w-20w条记录放入第二个文件,第20w-30w放入第三个文件,等。后续要检索数据,只需要选择其中一个文件遍历10w次,不需要再遍历100w次了
分区类型
MySQL支持下面几种分区:
- 范围分区
CREATE TABLE members (
firstname VARCHAR(25) NOT NULL,
lastname VARCHAR(25) NOT NULL,
username VARCHAR(16) NOT NULL,
email VARCHAR(35),
joined DATE NOT NULL
)
PARTITION BY RANGE( YEAR(joined) ) (
PARTITION p0 VALUES LESS THAN (1960),
PARTITION p1 VALUES LESS THAN (1970),
PARTITION p2 VALUES LESS THAN (1980),
PARTITION p3 VALUES LESS THAN (1990),
PARTITION p4 VALUES LESS THAN MAXVALUE
);
按照字段joined的值将表members分成5个文件,如果小于1960则数据进入p0文件,如果小于1970,则数据进入p1文件:
- 列表分区
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,
store_id INT
)
PARTITION BY LIST(store_id) (
PARTITION pNorth VALUES IN (3,5,6,9,17),
PARTITION pEast VALUES IN (1,2,10,11,19,20),
PARTITION pWest VALUES IN (4,12,13,14,18),
PARTITION pCentral VALUES IN (7,8,15,16)
);
对store_id进行分区,当store_id 等于3,5,6,9,17中一个,则数据放入pNorth,当store_id等于1,2,10,11,19,20中一个,则数据放入pEast
-
列分区
范围分区和列表分区的综合,对分区列支持多种数据格式。 -
hash分区
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,
store_id INT
)
PARTITION BY HASH(store_id)
PARTITIONS 4;
针对store_id的hash值进行分区,PARTITIONS 4表明总共有4个分区。
- key分区
CREATE TABLE k1 (
id INT NOT NULL PRIMARY KEY,
name VARCHAR(20)
)
PARTITION BY KEY()
PARTITIONS 2;
按照主键/唯一键进行分区,这种分区类似于hash分区,但是key的hash算法只能由系统指定。
- 子分区
CREATE TABLE ts (id INT, purchased DATE)
PARTITION BY RANGE( YEAR(purchased) )
SUBPARTITION BY HASH( TO_DAYS(purchased) )
SUBPARTITIONS 2 (
PARTITION p0 VALUES LESS THAN (1990),
PARTITION p1 VALUES LESS THAN (2000),
PARTITION p2 VALUES LESS THAN MAXVALUE
);
子分区,是将分区表在进行分区,先根据purchased进行范围分区(p0,p1,p2),在每个分区内部,在按照purchased的hash进行分2个区:
工作过程
从上面我们可以看出,一个分区表室友是由多个相关的底层表(p0,p1,p2)实现的,我们可以直接访问各个分区表。所有的底层表都必须使用相同的存储引擎,从存储引擎的角度看,底层表和普通表没有什么不一样
select查询
当查询一个分区表的时候,分区层先打开并锁住所有的底层表,优化器先判断是否可以过滤部分分区,然后再调用对应的存储引擎接口访问各个分区的数据
insert操作
当写入一条记录的时候,分区层先打开并锁住所有的底层表,然后确定哪个分区接受这条记录,再将记录写入对应底层表
delete操作
当删除一条记录时,分区层先打开并锁住所有的底层表,然后确定数据对应的分区,最后对相应底层表进行删除操作
update操作
当更新一条记录时,分区层先打开并锁住所有的底层表,mysql先确定需要更新的记录再哪个分区,然后取出数据并更新,再判断更新后的数据应该再哪个分区,最后对底层表进行写入操作,并对源数据所在的底层表进行删除操作
虽然每个操作都会“先打开并锁住所有的底层表”,但这并不是说分区表在处理过程中是锁住全表的,如果存储引擎能够自己实现行级锁,例如innodb,则会在分区层释放对应表锁。
注意项
- null值会无法使用分区进行过滤,只能全表扫描
- 如果分区列和索引列不匹配,查询无法进行分区过滤,很好理解,如果列不一致,那查询时,优化器到底是应该用索引呢还是用分区呢?
- 如果分区的颗粒度很细,有可能会提高选择分区的成本
- 操作数据时,需要打开或者锁住底层表(表锁,或者行锁),维护的成本的可能会比较高