標籤:線上mysql某個曆史資料表的分區筆記
背景:
線上的一個曆史資料庫,業務方反饋經常遇到一個範圍查詢就導致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` `order_his_nopart`;
rename table `order_his_tmp` `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每月自動建立一個新的分區。
使用預存程序的方法這裡先略過,後期補充。
本文出自 “菜雞” 部落格,請務必保留此出處http://lee90.blog.51cto.com/10414478/1917933
線上MySQL某個曆史資料表的分區筆記