线上MySQL某个历史数据表的分区笔记
2017-04-20 22:18
288 查看
背景:
线上的一个历史数据库,业务方反馈经常遇到一个范围查询就导致CPU迅速飙升的情况。拿到他们提供的SQL后,SQL类似下面这种:
select * from `order_his` where `xxxx` = '222' AND `XXXX` <> 1 AND order_time > '2016-11-01 00:00:00' AND order_time < '2017-06-01 00:00:00' \G
explain看了下发现基本上是全表扫描了,效率太低了,并且他们都是按月查询的,因此我们就对这张表按月进行分区,就能大大减少扫描的行数。
注意:TIMESTAMP类型的列,只能基于UNIX_TIMESTAMP函数进行分区,切记!
### 原始order_his表类似如下这种结构:
CREATE TABLE `order_his` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`order_time` timestamp NULL DEFAULT NULL,
`pay_time` timestamp NULL DEFAULT NULL,
`create_time` timestamp NULL DEFAULT NULL,
`update_time` timestamp NULL DEFAULT NULL,
PRIMARY KEY (`id`),
) ENGINE=InnoDB AUTO_INCREMENT=47603581 DEFAULT CHARSET=utf8;
step0 创建一个表结构和原先的表一样的tmp表
create table `order_his_tmp` like `order_his`;
step1 修改原有的主键,将分区键添加到主键里。
alter table `order_his_tmp` drop primary key,add primary key(id,order_time);
必须把分区键加到主键里面,不然step2也会报错提醒你这样做的。
step2 分区操作
ALTER TABLE `order_his_tmp` PARTITION BY RANGE (UNIX_TIMESTAMP (order_time))
(
PARTITION P201601 VALUES LESS THAN (UNIX_TIMESTAMP('2016-02-01')) ,
PARTITION P201602 VALUES LESS THAN (UNIX_TIMESTAMP('2016-03-01')) ,
PARTITION P201603 VALUES LESS THAN (UNIX_TIMESTAMP('2016-04-01')) ,
PARTITION P201604 VALUES LESS THAN (UNIX_TIMESTAMP('2016-05-01')) ,
PARTITION P201605 VALUES LESS THAN (UNIX_TIMESTAMP('2016-06-01')) ,
PARTITION P201606 VALUES LESS THAN (UNIX_TIMESTAMP('2016-07-01')) ,
PARTITION P201607 VALUES LESS THAN (UNIX_TIMESTAMP('2016-08-01')) ,
PARTITION P201608 VALUES LESS THAN (UNIX_TIMESTAMP('2016-09-01')) ,
PARTITION P201609 VALUES LESS THAN (UNIX_TIMESTAMP('2016-10-01')) ,
PARTITION P201610 VALUES LESS THAN (UNIX_TIMESTAMP('2016-11-01')) ,
PARTITION P201611 VALUES LESS THAN (UNIX_TIMESTAMP('2016-12-01')) ,
PARTITION P201612 VALUES LESS THAN (UNIX_TIMESTAMP('2017-01-01')) ,
PARTITION P201701 VALUES LESS THAN (UNIX_TIMESTAMP('2017-02-01')) ,
PARTITION P201702 VALUES LESS THAN (UNIX_TIMESTAMP('2017-03-01')) ,
PARTITION P201703 VALUES LESS THAN (UNIX_TIMESTAMP('2017-04-01')) ,
PARTITION P201704 VALUES LESS THAN (UNIX_TIMESTAMP('2017-05-01')) ,
PARTITION P201705 VALUES LESS THAN (UNIX_TIMESTAMP('2017-06-01')) ,
PARTITION P201706 VALUES LESS THAN (UNIX_TIMESTAMP('2017-07-01'))
);
step3、将原先表的数据灌入新的tmp表
insert into `order_his_tmp` select * from `order_his`;
step4、查询验证
explain partitions select * from `order_his_tmp` where `xxxx` = '222' AND `XXXX` <> 1 AND order_time > '2015-11-01 00:00:00' AND order_time < '2015-12-21 00:00:00' \G
*************************** 1. row ***************************
id: 1
select_type: SIMPLE
table: order_his
partitions: p201511,p201512 ### 可以看到这里走的是2015年11和12月,这2个分区
...........部分内容省略.............
注意: 当时在线上操作的时候,发现即使做了分区,执行计划里面显示的还是ALL全表扫描了,于是根据这个SELECT 加了个索引解决了这个问题。这里没有真实环境不好贴图出来。
step5、替换原先的表
通知开发同学当前不要对`order_his`表执行查询操作。
然后我们执行:
rename table `order_his` to `order_his_nopart`;rename table `order_his_tmp` to `order_his`;这样的话,新的`order_his`表就是分区表啦。
step6、添加分区表
后期如果需要加分区的话,只要执行如下这种操作就可以添加一个新的分区
ALTER TABLE `order_his` ADD PARTITION ( PARTITION P201707 VALUES LESS THAN (UNIX_TIMESTAMP('2017-08-01'))) ;
当然,如果我们想省事的话,就在step2的时候,一次性多创建很多分区(我当时是按月建分区,一直创建到2019年)
。
此外,也可以写个存储过程配合event_schedule每月自动创建一个新的分区。
使用存储过程的方法这里先略过,后期补充。
线上的一个历史数据库,业务方反馈经常遇到一个范围查询就导致CPU迅速飙升的情况。拿到他们提供的SQL后,SQL类似下面这种:
select * from `order_his` where `xxxx` = '222' AND `XXXX` <> 1 AND order_time > '2016-11-01 00:00:00' AND order_time < '2017-06-01 00:00:00' \G
explain看了下发现基本上是全表扫描了,效率太低了,并且他们都是按月查询的,因此我们就对这张表按月进行分区,就能大大减少扫描的行数。
注意:TIMESTAMP类型的列,只能基于UNIX_TIMESTAMP函数进行分区,切记!
### 原始order_his表类似如下这种结构:
CREATE TABLE `order_his` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`order_time` timestamp NULL DEFAULT NULL,
`pay_time` timestamp NULL DEFAULT NULL,
`create_time` timestamp NULL DEFAULT NULL,
`update_time` timestamp NULL DEFAULT NULL,
PRIMARY KEY (`id`),
) ENGINE=InnoDB AUTO_INCREMENT=47603581 DEFAULT CHARSET=utf8;
step0 创建一个表结构和原先的表一样的tmp表
create table `order_his_tmp` like `order_his`;
step1 修改原有的主键,将分区键添加到主键里。
alter table `order_his_tmp` drop primary key,add primary key(id,order_time);
必须把分区键加到主键里面,不然step2也会报错提醒你这样做的。
step2 分区操作
ALTER TABLE `order_his_tmp` PARTITION BY RANGE (UNIX_TIMESTAMP (order_time))
(
PARTITION P201601 VALUES LESS THAN (UNIX_TIMESTAMP('2016-02-01')) ,
PARTITION P201602 VALUES LESS THAN (UNIX_TIMESTAMP('2016-03-01')) ,
PARTITION P201603 VALUES LESS THAN (UNIX_TIMESTAMP('2016-04-01')) ,
PARTITION P201604 VALUES LESS THAN (UNIX_TIMESTAMP('2016-05-01')) ,
PARTITION P201605 VALUES LESS THAN (UNIX_TIMESTAMP('2016-06-01')) ,
PARTITION P201606 VALUES LESS THAN (UNIX_TIMESTAMP('2016-07-01')) ,
PARTITION P201607 VALUES LESS THAN (UNIX_TIMESTAMP('2016-08-01')) ,
PARTITION P201608 VALUES LESS THAN (UNIX_TIMESTAMP('2016-09-01')) ,
PARTITION P201609 VALUES LESS THAN (UNIX_TIMESTAMP('2016-10-01')) ,
PARTITION P201610 VALUES LESS THAN (UNIX_TIMESTAMP('2016-11-01')) ,
PARTITION P201611 VALUES LESS THAN (UNIX_TIMESTAMP('2016-12-01')) ,
PARTITION P201612 VALUES LESS THAN (UNIX_TIMESTAMP('2017-01-01')) ,
PARTITION P201701 VALUES LESS THAN (UNIX_TIMESTAMP('2017-02-01')) ,
PARTITION P201702 VALUES LESS THAN (UNIX_TIMESTAMP('2017-03-01')) ,
PARTITION P201703 VALUES LESS THAN (UNIX_TIMESTAMP('2017-04-01')) ,
PARTITION P201704 VALUES LESS THAN (UNIX_TIMESTAMP('2017-05-01')) ,
PARTITION P201705 VALUES LESS THAN (UNIX_TIMESTAMP('2017-06-01')) ,
PARTITION P201706 VALUES LESS THAN (UNIX_TIMESTAMP('2017-07-01'))
);
step3、将原先表的数据灌入新的tmp表
insert into `order_his_tmp` select * from `order_his`;
step4、查询验证
explain partitions select * from `order_his_tmp` where `xxxx` = '222' AND `XXXX` <> 1 AND order_time > '2015-11-01 00:00:00' AND order_time < '2015-12-21 00:00:00' \G
*************************** 1. row ***************************
id: 1
select_type: SIMPLE
table: order_his
partitions: p201511,p201512 ### 可以看到这里走的是2015年11和12月,这2个分区
...........部分内容省略.............
注意: 当时在线上操作的时候,发现即使做了分区,执行计划里面显示的还是ALL全表扫描了,于是根据这个SELECT 加了个索引解决了这个问题。这里没有真实环境不好贴图出来。
step5、替换原先的表
通知开发同学当前不要对`order_his`表执行查询操作。
然后我们执行:
rename table `order_his` to `order_his_nopart`;rename table `order_his_tmp` to `order_his`;这样的话,新的`order_his`表就是分区表啦。
step6、添加分区表
后期如果需要加分区的话,只要执行如下这种操作就可以添加一个新的分区
ALTER TABLE `order_his` ADD PARTITION ( PARTITION P201707 VALUES LESS THAN (UNIX_TIMESTAMP('2017-08-01'))) ;
当然,如果我们想省事的话,就在step2的时候,一次性多创建很多分区(我当时是按月建分区,一直创建到2019年)
。
此外,也可以写个存储过程配合event_schedule每月自动创建一个新的分区。
使用存储过程的方法这里先略过,后期补充。
相关文章推荐
- RDS for MySQL 通过分区归档历史数据
- RDS for MySQL 通过分区归档历史数据
- mysql partition(mysql range partition,对历史数据建分区)
- (笔记)Mysql命令drop table:删除数据表
- MySQL Infobright 数据仓库快速安装笔记
- MySQL笔记 操作,修改数据表、约束
- MySql数据分区操作之新增分区操作
- MySQL之——数据表分区技术PARTITION浅析
- MySQL学习笔记 第八讲:增加、删除、更新数据和备份还原
- 笔记:mysql修改数据表表名sql语句。
- 多IDC数据分布--MySQL多机房部署 - 学习笔记 - 51CTO技术博客
- mysql笔记-- mysql命令札记8 --MySql 数据表类型
- mysql学习笔记(5)修改数据表
- HIVE的安装配置、mysql的安装、hive创建表、创建分区、修改表等内容、hive beeline使用、HIVE的四种数据导入方式、使用Java代码执行hive的sql命令
- MySQL学习笔记12:数据类型
- Mysql笔记之 数据类型
- MySQL学习笔记十二:数据备份与恢复
- MySQL笔记之数据类型详解
- MySQL笔记之数据备份与还原的使用详解
- MYSQL5.0 脚本测试笔记【复制表结构和数据SQL语句】