首先来看以正常表的存放方式
上面一部分是innodb引擎的,下面一部分是myISAM引擎的.
customer_login_log.frm #保存了了每个表的元数据,包括表结构以及相应的定义,⽆无论是什什么数
据引擎都有这个⽂文件
customer_login_log.ibd #innodb的存放数据⽂文件和索引的地⽅方
如果表的数据太大,可能一个磁盘放不下,这个时候,我们可以把数据分配到不同的磁盘里面去。
表分区,是指根据一定规则,将数据库中的一张表分解成多个更小的,容易管理的部分。从逻辑上看,只有一张表,但是底层却是由多个物理分区组成。
MySQL数据库在5.1版本时添加了对分区的支持,分区的过程是将一个表或索引分解为多个更小、更可管理的部分。就访问数据库的应用而言,从逻辑上讲,只有一个表或一个索引,但是在物理上这个表或索引可能由数十个物理分区组成。每个分区都是独立的对象,可以独自处理,也可以作为一个更大对象的一部分进行处理。
查看数据库是否支持分区表
# MySQL v5.6以前
show variables like '%partition%';
# MySQL v5.6(含)版本后
mysql>show plugins;
CREATE TABLE `customer_login_log` (
`customer_id` int(10) unsigned NOT NULL COMMENT '登录⽤用户ID',
`login_time` datetime NOT NULL COMMENT '⽤用户登录时间',
`login_ip` int(10) unsigned NOT NULL COMMENT '登录IP',
`login_type` tinyint(4) NOT NULL COMMENT '登录类型:0未成功 1成功'
) ENGINE=InnoDB DEFAULT CHARSET=utf8
PARTITION BY HASH(customer_id) PARTITIONS 4;
insert into customer_login_log values(1,'2020-06-20 22:30:01',1,1);
insert into customer_login_log values(2,'2020-06-20 22:30:02',2,1);
insert into customer_login_log values(3,'2020-06-20 22:30:03',3,1);
insert into customer_login_log values(4,'2020-06-20 22:30:04',4,1);
使⽤用函数进⾏行行INT输出
CREATE TABLE `customer_login_log` (
`customer_id` int(10) unsigned NOT NULL COMMENT '登录⽤用户ID',
`login_time` timestamp NOT NULL COMMENT '⽤用户登录时间', #注意这⾥里里的数据类型
`login_ip` int(10) unsigned NOT NULL COMMENT '登录IP',
`login_type` tinyint(4) NOT NULL COMMENT '登录类型:0未成功 1成功'
) ENGINE=InnoDB DEFAULT CHARSET=utf8
PARTITION BY HASH(UNIX_TIMESTAMP(login_time)) PARTITIONS 4;
转换函数
ABS()
CEILING() (see CEILING() and FLOOR())
DAY()
DAYOFMONTH()
DAYOFWEEK()
DAYOFYEAR()
DATEDIFF()
EXTRACT() (see EXTRACT() function with WEEK specifier)
FLOOR() (seeCEILING() and FLOOR())
HOUR()
MICROSECOND()
MINUTE()
MOD()
MONTH()
QUARTER()
SECOND()
TIME_TO_SEC()
TO_DAYS()
TO_SECONDS()
UNIX_TIMESTAMP() (permitted beginning with MySQL 5.6.1 and fully supported
beginning with MySQL 5.6.3, with TIMESTAMP columns)
WEEKDAY()
YEAR()
YEARWEEK()
创建以后的内容
查看分区表是否创建成功
mysql> explain partitions select * from customer_login_log;
查询每个分区存放多少数据
select table_name,partition_name,partition_description,table_rows from information_schema.PARTITIONS where table_name='customer_login_log';
查询具体的某个分区数据
select * from customer_login_log partition(p1,p2);
select * from customer_login_log partition(p3) where customer_id=3;
按照分区键取值的列表进行分区
各分区的列表值不能重复
每一行数据必须找到对应的分区列列表,否则插入失败
CREATE TABLE `customer_login_log_list` (
`customer_id` int(10) unsigned NOT NULL COMMENT '登录用户ID',
`login_time` datetime NOT NULL COMMENT '用户登录时间',
`login_ip` int(10) unsigned NOT NULL COMMENT '登录IP',
`login_type` tinyint(4) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8
PARTITION BY LIST(login_type)(
PARTITION jishu VALUES in (1,3,5,7,9),
PARTITION oushu VALUES in (2,4,6,8)
);
insert into customer_login_log_list values(1,'2020-06-20 22:30:01',1,1);
insert into customer_login_log_list values(2,'2020-06-20 22:30:02',2,2);
insert into customer_login_log_list values(3,'2020-06-20 22:30:03',3,3);
insert into customer_login_log_list values(4,'2020-06-20 22:30:04',4,4);
如果分区键不在分区中
mysql> insert into customer_login_log_list values(4,'2020-06-20 22:30:04',4,0);
ERROR 1526 (HY000): Table has no partition for value 0
根据分区的不同范围值将数据放不同文件中
多个分区要连续,不能重叠
要有封口的MAXVALUE
CREATE TABLE `customer_login_log_range` (
`customer_id` int(10) unsigned NOT NULL COMMENT '登录用户ID',
`login_time` datetime NOT NULL COMMENT '用户登录时间',
`login_ip` int(10) unsigned NOT NULL COMMENT '登录IP',
`login_type` tinyint(4) NOT NULL COMMENT '登录类型:0未成功 1成功'
) ENGINE=InnoDB DEFAULT CHARSET=utf8
PARTITION BY RANGE(YEAR(login_time))(
PARTITION y2017 VALUES LESS THAN (2017),
PARTITION y2018 VALUES LESS THAN (2018),
PARTITION y2019 VALUES LESS THAN (2019),
PARTITION y2020 VALUES LESS THAN (2020),
PARTITION maxyear VALUES LESS THAN MAXVALUE
);
insert into customer_login_log_range values(1,'2016-06-20 22:30:01',1,1);
insert into customer_login_log_range values(2,'2017-06-20 22:30:02',2,2);
insert into customer_login_log_range values(3,'2018-06-20 22:30:03',3,3);
insert into customer_login_log_range values(5,'2019-06-20 22:30:04',4,4);
insert into customer_login_log_range values(6,'2020-06-20 22:30:04',4,4);
insert into customer_login_log_range values(7,'2021-06-20 22:30:04',4,4);
insert into customer_login_log_range values(8,'2022-06-20 22:30:04',4,4);
insert into customer_login_log_range values(9,'2023-06-20 22:30:04',4,4);
如果我们需要后期新增range分区,就不能maxvalue封口
CREATE TABLE `customer_login_log_range1` (
`customer_id` int(10) unsigned NOT NULL COMMENT '登录用户ID',
`login_time` datetime NOT NULL COMMENT '用户登录时间',
`login_ip` int(10) unsigned NOT NULL COMMENT '登录IP',
`login_type` tinyint(4) NOT NULL COMMENT '登录类型:0未成功 1成功'
) ENGINE=InnoDB DEFAULT CHARSET=utf8
PARTITION BY RANGE(YEAR(login_time))(
PARTITION y2017 VALUES LESS THAN (2017),
PARTITION y2018 VALUES LESS THAN (2018),
PARTITION y2019 VALUES LESS THAN (2019),
PARTITION y2020 VALUES LESS THAN (2020)
);
alter table customer_login_log_range1 add PARTITION(
PARTITION y2021 VALUES LESS THAN (2021),
PARTITION y2022 VALUES LESS THAN (2022),
PARTITION y2023 VALUES LESS THAN (2023)
)
一个没有创建分区的表
CREATE TABLE `customer_login_log_range_no` (
`customer_id` int(10) unsigned NOT NULL COMMENT '登录用户ID',
`login_time` datetime NOT NULL COMMENT '用户登录时间',
`login_ip` int(10) unsigned NOT NULL COMMENT '登录IP',
`login_type` tinyint(4) NOT NULL COMMENT '登录类型:0未成功 1成功'
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
insert into customer_login_log_range_no values(1,'2016-06-20 22:30:01',1,1);
insert into customer_login_log_range_no values(2,'2017-06-20 22:30:02',2,2);
insert into customer_login_log_range_no values(3,'2018-06-20 22:30:03',3,3);
insert into customer_login_log_range_no values(5,'2019-06-20 22:30:04',4,4);
insert into customer_login_log_range_no values(6,'2020-06-20 22:30:04',4,4);
insert into customer_login_log_range_no values(7,'2021-06-20 22:30:04',4,4);
insert into customer_login_log_range_no values(8,'2022-06-20 22:30:04',4,4);
insert into customer_login_log_range_no values(9,'2023-06-20 22:30:04',4,4);
alter table customer_login_log_range_no PARTITION BY RANGE(YEAR(login_time))(
PARTITION y2017 VALUES LESS THAN (2017),
PARTITION y2018 VALUES LESS THAN (2018),
PARTITION y2019 VALUES LESS THAN (2019),
PARTITION y2020 VALUES LESS THAN (2020),
PARTITION y2021 VALUES LESS THAN (2021),
PARTITION y2022 VALUES LESS THAN (2022),
PARTITION maxyear VALUES LESS THAN MAXVALUE
)
# 数据会按照分区规则进行数据重新组装,数据会进入相应分区
如果要删除分区使用命令,不能直接删除文集
alter table customer_login_log_range drop partition y2017;
日志数据进行备份的时候,在MySQL v5.7版本后加入了一个分区交换的概念
表结构相同
归档的这个表不能是分区表
归档表不能有外键约束
ARCHIVE
#归档表
CREATE TABLE `arch_customer_login_log_2016` (
`customer_id` int(10) unsigned NOT NULL COMMENT '登录⽤用户ID',
`login_time` datetime NOT NULL COMMENT '⽤用户登录时间',
`login_ip` int(10) unsigned NOT NULL COMMENT '登录IP',
`login_type` tinyint(4) NOT NULL COMMENT '登录类型:0未成功 1成功'
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
ALTER TABLE customer_login_log exchange PARTITION p0 WITH TABLE
arch_customer_login_log_2016;
ALTER TABLE arch_customer_login_log_2016 ENGINE=ARCHIVE;
删除分区文件
ALTER TABLE customer_login_log DROP PARTITION p0;
如果一个分区表有主键,那么分区键一定要是主键的一部分
CREATE TABLE `im_product_variants` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`variants_id` varchar(50) NOT NULL DEFAULT '',
`variants_title` varchar(100) NOT NULL DEFAULT '' COMMENT '属性名称,多个属性值⽤用/隔开',
`sku` varchar(50) NOT NULL DEFAULT '',
`original_price` decimal(10,2) NOT NULL COMMENT '原价',
`sale_price` decimal(10,2) NOT NULL COMMENT '售价',
`variants_weight` int(11) NOT NULL COMMENT '重量量单位g',
`option_name1` varchar(50) NOT NULL DEFAULT '' COMMENT '属性名1',
`option_name2` varchar(50) DEFAULT NULL COMMENT '属性名2',
`option_name3` varchar(50) DEFAULT NULL COMMENT '属性名3',
`option_value1` varchar(50) NOT NULL DEFAULT '' COMMENT '属性值1',
`option_value2` varchar(50) DEFAULT NULL COMMENT '属性值2',
`option_value3` varchar(50) DEFAULT NULL COMMENT '属性值3',
`product_id` varchar(50) NOT NULL DEFAULT '' COMMENT '产品id',
`image_src` varchar(300) NOT NULL DEFAULT '' COMMENT '属性图⽚片src路路径',
`create_emp` varchar(50) NOT NULL DEFAULT '',
`update_emp` varchar(50) NOT NULL DEFAULT '',
`create_time` int(11) NOT NULL,
`update_time` int(11) NOT NULL,
PRIMARY KEY (`id`,`create_time`)
) ENGINE=InnoDB AUTO_INCREMENT=11 DEFAULT CHARSET=utf8
PARTITION BY RANGE(create_time)(
PARTITION p0 VALUES LESS THAN (1483200000),
PARTITION p1 VALUES LESS THAN (1514736000),
PARTITION p2 VALUES LESS THAN (1546272000),
PARTITION p3 VALUES LESS THAN (1577808000)
);
如果一个表不是分区表并且已经有数据了,还能不能变成分区表?
答案是可以
ALTER TABLE customer_login_log PARTITION BY RANGE(YEAR(login_time))(
PARTITION p0 VALUES LESS THAN (2017),
PARTITION p1 VALUES LESS THAN (2018),
PARTITION p2 VALUES LESS THAN (2019),
PARTITION p3 VALUES LESS THAN (2020)
);
变成分区表后,数据是否按照分区重新排列?
答案是会重排数据,但数据量大的情况下I/O消耗很大
这种数据量大的表,要按照修改表结构的方式进行分区表操作
既然把表分开了,那我们分区表最大有多少个?
答案是1024个
日志表的特征
在这样的表结构下分区表就是我们的最佳实践