MySQL表RANGE分区,新增,删除,重组,效率对比

如需了解MySQL分区相关概念,使用场景以及限制等,点击这里

本文示例表

创建表

CREATE TABLE `student_1` (
  `ID` int(11) NOT NULL AUTO_INCREMENT,
  `NAME` varchar(30) NOT NULL,
  `SEX` char(2) NOT NULL,
  `AGE` int(11) NOT NULL,
  `CLASS` varchar(10) NOT NULL,
  `GRADE` varchar(20) NOT NULL,
  `HOBBY` varchar(100) DEFAULT NULL,
  `CREATE_TIME` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
  PRIMARY KEY (`ID`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;

插入两千万数据

DROP PROCEDURE IF EXISTS inst_data;
CREATE PROCEDURE inst_data() BEGIN
	DECLARE incr INT DEFAULT 1;
	WHILE incr <= 20000000 DO
	  INSERT INTO `study`.`student_1` (`NAME`, `SEX`, `AGE`, `CLASS`, `GRADE`, `HOBBY` ,`CREATE_TIME`)
		VALUES
			(
				CONCAT( 'jsontom', incr),
			  CASE WHEN incr % 2 = 0 THEN '男' ELSE '女' END,
				FLOOR(18 + ( RAND() * 7 )),
				CONCAT(1 + FLOOR(RAND() * 10 ), '班' ),
				CONCAT(1 + FLOOR(RAND() * 10 ), '年级' ),
				CONCAT( '喜爱看第',1 + FLOOR(RAND() * 9 ),'个片儿'),
				NOW()
			);		
	  SET incr = incr + 1;
	END WHILE;
	SELECT CONCAT('插入条数 ',incr);
END;
CALL inst_data ( );

range分区管理

当用某些字段作为分区字段时,则分区字段必须全部包含在主键或者某一个唯一索引中。

上表如果直接添加分区会报如下错

1503 - A PRIMARY KEY must include all columns in the table's partitioning function

本文示例表的分区字段为CREATE_TIME,该表除了主键id没有其他唯一索引。

第一种方式我们把id和CREATE_TIME字段作为联合主键。

第二种方式是新建一个唯一索引包含CREATE_TIME字段。

本文我们采用第一种方式,现实中根据实际情况进行选择。

分区的时间值请根据实际值修改。

创建表时进行分区

CREATE TABLE `student_1` (
  `ID` int(11) NOT NULL AUTO_INCREMENT,
  `NAME` varchar(30) NOT NULL,
  `SEX` char(2) NOT NULL,
  `AGE` int(11) NOT NULL,
  `CLASS` varchar(10) NOT NULL,
  `GRADE` varchar(20) NOT NULL,
  `HOBBY` varchar(100) DEFAULT NULL,
  `CREATE_TIME` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
  PRIMARY KEY (`ID`,`CREATE_TIME`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=20000001 DEFAULT CHARSET=utf8
PARTITION BY RANGE ( UNIX_TIMESTAMP(CREATE_TIME) )
(
PARTITION p1 VALUES LESS THAN ( UNIX_TIMESTAMP('2021-11-19 10:04:42') ),
PARTITION p2 VALUES LESS THAN ( UNIX_TIMESTAMP('2021-11-19 12:44:02') ),
PARTITION p3 VALUES LESS THAN ( UNIX_TIMESTAMP('2021-11-19 15:23:22') ),
PARTITION p4 VALUES LESS THAN ( UNIX_TIMESTAMP('2021-11-19 18:02:42') ),
PARTITION p5 VALUES LESS THAN ( UNIX_TIMESTAMP('2021-11-19 20:42:03') ),
PARTITION pmax VALUES LESS THAN (UNIX_TIMESTAMP('2021-11-21 00:00:00'))
);

为现有数据的表进行分区

分区语句如下

ALTER TABLE student_1
PARTITION BY RANGE ( UNIX_TIMESTAMP(CREATE_TIME) )
(
PARTITION p1 VALUES LESS THAN ( UNIX_TIMESTAMP('2021-11-19 10:04:42') ),
PARTITION p2 VALUES LESS THAN ( UNIX_TIMESTAMP('2021-11-19 12:44:02') ),
PARTITION p3 VALUES LESS THAN ( UNIX_TIMESTAMP('2021-11-19 15:23:22') ),
PARTITION p4 VALUES LESS THAN ( UNIX_TIMESTAMP('2021-11-19 18:02:42') ),
PARTITION p5 VALUES LESS THAN ( UNIX_TIMESTAMP('2021-11-19 20:42:03') ),
PARTITION pmax VALUES LESS THAN (UNIX_TIMESTAMP('2021-11-21 00:00:00'))
);

新增分区

ALTER TABLE student_1
ADD PARTITION (PARTITION p6 VALUES LESS THAN ( UNIX_TIMESTAMP('2021-11-26 20:42:03') ));

重组分区

注意:合并/拆分分区后,产生的新分区,查询新分区数据量为零,但是数据是真实存在的。

合并分区

把p1和p2分区合并成p1_p2分区

ALTER TABLE student_1
REORGANIZE PARTITION p1,p2 INTO
(
PARTITION p1_p2 VALUES LESS THAN ( UNIX_TIMESTAMP('2021-11-19 12:44:02') )
);

拆分分区

把p1_p2分区拆分成p1和p2分区

ALTER TABLE student_1
REORGANIZE PARTITION p1_p2 INTO
(
PARTITION p1 VALUES LESS THAN ( UNIX_TIMESTAMP('2021-11-19 10:04:42') ),
PARTITION p2 VALUES LESS THAN ( UNIX_TIMESTAMP('2021-11-19 12:44:02') )
);

查看分区的数据量

SELECT
	PARTITION_NAME,
	TABLE_ROWS 
FROM
	INFORMATION_SCHEMA.PARTITIONS 
WHERE
	TABLE_NAME = 'student_1';

删除分区

注意:当删除一个分区,该分区中所有的数据同时也被删除了。

ALTER TABLE student_1 DROP PARTITION p1;

取消分区

该种方式数据得以保留,恢复成普通表

alter table student_1 remove partitioning;

效率对比

对于如下SQL

select * from student_1 where CREATE_TIME = STR_TO_DATE('2021-11-19 18:02:42','%Y-%m-%d %H:%i:%s')

不使用索引和分区的查询

全表扫描查询用时16.960s
在这里插入图片描述

使用索引的查询

添加索引

alter table student_1 add index crtm(CREATE_TIME);

查询用时1.961s
在这里插入图片描述

使用分区查询

添加分区

ALTER TABLE student_1
PARTITION BY RANGE ( UNIX_TIMESTAMP(CREATE_TIME) )
(
PARTITION p1 VALUES LESS THAN ( UNIX_TIMESTAMP('2021-11-19 10:04:42') ),
PARTITION p2 VALUES LESS THAN ( UNIX_TIMESTAMP('2021-11-19 12:44:02') ),
PARTITION p3 VALUES LESS THAN ( UNIX_TIMESTAMP('2021-11-19 15:23:22') ),
PARTITION p4 VALUES LESS THAN ( UNIX_TIMESTAMP('2021-11-19 18:02:42') ),
PARTITION p5 VALUES LESS THAN ( UNIX_TIMESTAMP('2021-11-19 20:42:03') ),
PARTITION pmax VALUES LESS THAN (UNIX_TIMESTAMP('2021-11-21 00:00:00'))
);

查询耗时3.692s
在这里插入图片描述

使用分区+索引查询

对表进行分区,并对CREATE_TIME字段添加索引,查询耗时0.614s
在这里插入图片描述

总结

效率由高到低为

使用分区+索引 > 使用索引 > 使用分区 > 不使用索引和分区

所以在使用索引效果不佳或者是索引维护代价高昂时,我们可以考虑引入分区的方式提升效率。

注意
访问分区表时,很重要的一点是在WHERE条件中带上分区列(不能是包含列的表达式),有时候看似多余的也要带上,这样可以让优化器能够过滤掉无须访问的分区。如果没有这样的条件,会访问所有的分区,代价高昂。

EXPLAIN PARTITIONS可以查看SQL语句是否执行了分区过滤

猜你喜欢

转载自blog.csdn.net/weixin_43073775/article/details/121332765