#首先上表結構mysql
CREATE TABLE `sys_history` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`did` bigint(20) NOT NULL ,
`ndata` int(11) NOT NULL DEFAULT '0' ,
`create_time` datetime NOT NULL,
`update_time` datetime DEFAULT NULL,
`is_deleted` int(11) DEFAULT '0',
PRIMARY KEY (`id`,`did`,`create_time`),
UNIQUE KEY `hindex` (`id`,`did`,`create_time`)
) ENGINE=InnoDB AUTO_INCREMENT=0 DEFAULT CHARSET=utf8
/*!50500 PARTITION BY RANGE COLUMNS(create_time)
(PARTITION p2018061300 VALUES LESS THAN ('2018-06-13 00:00:00') ENGINE = InnoDB,
PARTITION p2018061302 VALUES LESS THAN ('2018-06-13 02:00:00') ENGINE = InnoDB,
PARTITION p2018061304 VALUES LESS THAN ('2018-06-13 04:00:00') ENGINE = InnoDB,
PARTITION p2018061306 VALUES LESS THAN ('2018-06-13 06:00:00') ENGINE = InnoDB,
PARTITION p2018061308 VALUES LESS THAN ('2018-06-13 08:00:00') ENGINE = InnoDB) */
複製代碼
在建立表的時候,首先創建了
id
,did
,create_time
三個聯合索引,因爲個人數據中,did
和create_time
都會重複,可是三個合起來就確定不會重複,因此我就選擇了建立聯合索引。至於爲什麼這樣見索引就本身google或者度娘啦,這不是本文的重點。sql
這裏我使用了Range分區,由於咱們這裏設想的是按小時建立分區,這是一個時間範圍,範圍應該連續可是不重疊,使用
PARTITION BY RANGE
,VALUES LESS THAN
關鍵字。不使用COLUMNS
關鍵字時RANGE
括號內必須爲整數字段名或返回肯定整數的函數,添加COLUMNS
關鍵字可定義非integer
範圍及多列範圍,不過須要注意COLUMNS
括號內只能是列名,不支持函數;多列範圍時,多列範圍必須呈遞增趨勢。值得注意的是我建表的時候已經預先建立了幾個分區,舉例來講:PARTITION p2018061300 VALUES LESS THAN ('2018-06-13 00:00:00') ENGINE = InnoDB
,這裏我預先建立了一個名爲p2018061300
的分區,其LESS
值爲2018-06-13 00:00:00
,目的是想把create_time<=2018-06-13 00:00:00
的數據放入改分區,p2018061300
,p2018061302
,p2018061304
,p2018061306
從分區名也能夠看出每兩個小時一個分區,p2018061300
的意思就是2018年6月13日0點以前的數據的分區。bash
總不能每次都手動每兩小時建立分區吧...必須是自動的! 這時候存儲過程就要登場了!less
-- 自動建立表分區的存儲過程
DROP PROCEDURE IF EXISTS AUTO_PARTITION_HOUR;
DELIMITER //
CREATE PROCEDURE AUTO_PARTITION_HOUR(IN $table_name VARCHAR(64), IN $range_hours INT, IN $min_time VARCHAR(20))
BEGIN
-- $table_name 待分片的表名
-- $range_hours 每隔分區的跨度時長,例如2小時,儘可能取24能整除的數
-- $min_time 已有歷史數據的最小時間,決定起始分片的時間,若是爲 null 則自動取當天的零點
DECLARE $base_dir VARCHAR(64);
DECLARE $monthly_dir VARCHAR(64);
DECLARE $now VARCHAR(30); -- 當前時間戳
DECLARE $zero_am VARCHAR(30); -- 明天零點
DECLARE $stop_hour VARCHAR(30); -- 預建立終止時間戳
DECLARE $sql_partition_template VARCHAR(500); -- 建分片的 SQL 模板
DECLARE $partition_name VARCHAR(20); -- 新分片名字
DECLARE $last_less_than_hour VARCHAR(30); -- 上一個分片的 less 值
DECLARE $less_than_hour VARCHAR(30); -- 上一個分片的 less 值
DECLARE $sql_tmp VARCHAR(500); -- 臨時拼接的 SQL
-- 數據文件和索引文件的存放目錄
SET $base_dir = CONCAT('/data/mysql/hisdata/', DATABASE(), '/', $table_name, '/');
-- 當前系統時間
SET $now = DATE_FORMAT(now(), '%Y-%m-%d %H:%i:%s');
-- 今天零點
SET $zero_am = DATE_FORMAT(now(), '%Y-%m-%d %00:%00:%00');
-- 預建立分區的終止小時值(後天零點)
SET $stop_hour = DATE_FORMAT(now()+interval 172800 second, '%Y-%m-%d %00:%00:%00');
-- 建立新分片的SQL模板
SET $sql_partition_template = CHAR(10);
SET $sql_partition_template = CONCAT($sql_partition_template, 'ALTER TABLE ', $table_name, ' ADD PARTITION (');
SET $sql_partition_template = CONCAT($sql_partition_template, CHAR(10), 'PARTITION $partition_name VALUES LESS THAN (');
SET $sql_partition_template = CONCAT($sql_partition_template,'\'$less_than_hour\'',')');
SET $sql_partition_template = CONCAT($sql_partition_template, CHAR(10), ');');
-- 查找上一個分片的終止時間
SET $last_less_than_hour = NULL;
SELECT SUBSTRING(PARTITION_DESCRIPTION,2,19) INTO $last_less_than_hour FROM INFORMATION_SCHEMA.PARTITIONS
WHERE TABLE_SCHEMA=DATABASE() AND TABLE_NAME=$table_name
ORDER BY PARTITION_ORDINAL_POSITION DESC LIMIT 1;
IF $last_less_than_hour IS NULL OR $last_less_than_hour='' THEN
IF $min_time IS NULL THEN
-- 沒有記錄,設置爲當天零點
SET $last_less_than_hour = $zero_am;
ELSE
-- 設置爲已有最先記錄的當天零點
SET $last_less_than_hour = DATE_FORMAT($min_time, '%Y-%m-%d %00:%00:%00');
END IF;
END IF;
-- 循環預建立分區
_PARTITION_LOOP_ : LOOP
SET $less_than_hour = DATE_ADD($last_less_than_hour ,interval $range_hours HOUR);
IF $less_than_hour > $stop_hour THEN
LEAVE _PARTITION_LOOP_;
END IF;
SET $partition_name = CONCAT('p', DATE_FORMAT($less_than_hour, '%Y%m%d%H'));
SET $sql_tmp = $sql_partition_template;
SET $sql_tmp = REPLACE($sql_tmp, '$partition_name', $partition_name);
SET $sql_tmp = REPLACE($sql_tmp, '$less_than_hour', $less_than_hour);
SET @stmt_sql = $sql_tmp;
PREPARE stmt FROM @stmt_sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
SET $last_less_than_hour = $less_than_hour;
END LOOP _PARTITION_LOOP_;
END
//
DELIMITER ;
複製代碼
個人存儲過程是參考網上一位大牛的,網址忘了...Sorry 大體意思就是輸入
表名
,分片時間步進
,最先數據時間
,而後查找上一個分片的終止時間,若是沒有記錄就設置爲當天的0點
,或者最先記錄當天的0點
, 若是已經有分片記錄了,就取分片的終止時間,而後循環建立分區,每循環一次小時加上$range_hours
個小時,循環裏面有個條件跳出函數
IF $less_than_hour > $stop_hour THEN
LEAVE _PARTITION_LOOP_;
END IF;
複製代碼
這裏的$stop_hour
就是以前設置的後天的0點
。 這個存儲過程天天運行一次就能夠了。post
這個簡單啦,建立一個自動運行的
Event
就能夠了,上代碼google
-- 建立事件
create event auto_partition_event
on schedule every 1 DAY starts '2018-06-12 18:30:00'
ON COMPLETION PRESERVE ENABLE
do
call AUTO_PARTITION_HOUR('sys_history',2);
複製代碼
上一篇說到了要求保留一年的歷史數據,那麼就意味着須要定時刪除一年前的數據和分區(注意:刪除分區就是刪除數據),那麼想到的又是
存儲過程
,上代碼spa
-- 循環刪除分區
_CLEAR_PARTITION_LOOP_:LOOP
-- 查找最先的一個分區的時間
SET $most_less_than_hour = NULL;
SELECT SUBSTRING(PARTITION_DESCRIPTION,2,19) INTO $most_less_than_hour FROM INFORMATION_SCHEMA.PARTITIONS
WHERE TABLE_SCHEMA=DATABASE() AND TABLE_NAME=$table_name
ORDER BY PARTITION_ORDINAL_POSITION LIMIT 1;
SET $most_partition_name = CONCAT('p', DATE_FORMAT($most_less_than_hour, '%Y%m%d%H'));
SET $most_less_than_hour = DATE_FORMAT($most_less_than_hour, '%Y-%m-%d %00:%00:%00');
IF timestampdiff(day,$most_less_than_hour,$zero_am) < $delay_day THEN
LEAVE _CLEAR_PARTITION_LOOP_;
END IF;
SET $del_sql = CHAR(10);
SET $del_sql = CONCAT($del_sql,'ALTER TABLE ',$table_name,' DROP PARTITION ', $most_partition_name);
SET @sd_sql = $del_sql;
PREPARE sd FROM @sd_sql;
EXECUTE sd;
DEALLOCATE PREPARE sd;
END LOOP _CLEAR_PARTITION_LOOP_;
複製代碼
建立一個循環自動循環刪除時間大於輸入保留天數的數據,這裏多了一個輸入參數
$delay_day
,例如我只想保留365天的數據, 那麼$delay_day
輸入值就爲365。code
-- 自動建立表分區的存儲過程
DROP PROCEDURE IF EXISTS AUTO_PARTITION_HOUR;
DELIMITER //
CREATE PROCEDURE AUTO_PARTITION_HOUR(IN $table_name VARCHAR(64), IN $range_hours INT, IN $min_time VARCHAR(20),IN $delay_day INT)
BEGIN
-- $table_name 待分片的表名
-- $range_hours 每隔分區的跨度時長,例如2小時,儘可能取24能整除的數
-- $min_time 已有歷史數據的最小時間,決定起始分片的時間,若是爲 null 則自動取當天的零點
DECLARE $base_dir VARCHAR(64);
DECLARE $monthly_dir VARCHAR(64);
DECLARE $now VARCHAR(30); -- 當前時間戳
DECLARE $zero_am VARCHAR(30); -- 明天零點
DECLARE $stop_hour VARCHAR(30); -- 預建立終止時間戳
DECLARE $sql_partition_template VARCHAR(500); -- 建分片的 SQL 模板
DECLARE $partition_name VARCHAR(20); -- 新分片名字
DECLARE $last_less_than_hour VARCHAR(30); -- 上一個分片的 less 值
DECLARE $less_than_hour VARCHAR(30); -- 上一個分片的 less 值
DECLARE $sql_tmp VARCHAR(500); -- 臨時拼接的 SQL
DECLARE $most_less_than_hour VARCHAR(30); -- 最先一個分區的less 值
DECLARE $most_partition_name VARCHAR(30); -- 最先一個分區的名稱
DECLARE $del_sql VARCHAR(100);
-- 數據文件和索引文件的存放目錄
SET $base_dir = CONCAT('/data/mysql/hisdata/', DATABASE(), '/', $table_name, '/');
-- 當前系統時間
SET $now = DATE_FORMAT(now(), '%Y-%m-%d %H:%i:%s');
-- 今天零點
SET $zero_am = DATE_FORMAT(now(), '%Y-%m-%d %00:%00:%00');
-- 預建立分區的終止小時值(後天零點)
SET $stop_hour = DATE_FORMAT(now()+interval 172800 second, '%Y-%m-%d %00:%00:%00');
-- 建立新分片的SQL模板
SET $sql_partition_template = CHAR(10);
SET $sql_partition_template = CONCAT($sql_partition_template, 'ALTER TABLE ', $table_name, ' ADD PARTITION (');
SET $sql_partition_template = CONCAT($sql_partition_template, CHAR(10), 'PARTITION $partition_name VALUES LESS THAN (');
SET $sql_partition_template = CONCAT($sql_partition_template,'\'$less_than_hour\'',')');
SET $sql_partition_template = CONCAT($sql_partition_template, CHAR(10), ');');
-- 查找上一個分片的終止小時值
SET $last_less_than_hour = NULL;
SELECT SUBSTRING(PARTITION_DESCRIPTION,2,19) INTO $last_less_than_hour FROM INFORMATION_SCHEMA.PARTITIONS
WHERE TABLE_SCHEMA=DATABASE() AND TABLE_NAME=$table_name
ORDER BY PARTITION_ORDINAL_POSITION DESC LIMIT 1;
IF $last_less_than_hour IS NULL OR $last_less_than_hour='' THEN
IF $min_time IS NULL THEN
-- 沒有記錄,設置爲當天零點
SET $last_less_than_hour = $zero_am;
ELSE
-- 設置爲已有最先記錄的當天零點
SET $last_less_than_hour = DATE_FORMAT($min_time, '%Y-%m-%d %00:%00:%00');
END IF;
END IF;
-- 循環預建立分區
_PARTITION_LOOP_ : LOOP
SET $less_than_hour = DATE_ADD($last_less_than_hour ,interval $range_hours HOUR);
IF $less_than_hour > $stop_hour THEN
LEAVE _PARTITION_LOOP_;
END IF;
SET $partition_name = CONCAT('p', DATE_FORMAT($less_than_hour, '%Y%m%d%H'));
SET $sql_tmp = $sql_partition_template;
SET $sql_tmp = REPLACE($sql_tmp, '$partition_name', $partition_name);
SET $sql_tmp = REPLACE($sql_tmp, '$less_than_hour', $less_than_hour);
SET @stmt_sql = $sql_tmp;
PREPARE stmt FROM @stmt_sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
SET $last_less_than_hour = $less_than_hour;
END LOOP _PARTITION_LOOP_;
-- 循環刪除分區
_CLEAR_PARTITION_LOOP_:LOOP
-- 查找最先的一個分區的時間
SET $most_less_than_hour = NULL;
SELECT SUBSTRING(PARTITION_DESCRIPTION,2,19) INTO $most_less_than_hour FROM INFORMATION_SCHEMA.PARTITIONS
WHERE TABLE_SCHEMA=DATABASE() AND TABLE_NAME=$table_name
ORDER BY PARTITION_ORDINAL_POSITION LIMIT 1;
SET $most_partition_name = CONCAT('p', DATE_FORMAT($most_less_than_hour, '%Y%m%d%H'));
SET $most_less_than_hour = DATE_FORMAT($most_less_than_hour, '%Y-%m-%d %00:%00:%00');
IF timestampdiff(day,$most_less_than_hour,$zero_am) < $delay_day THEN
LEAVE _CLEAR_PARTITION_LOOP_;
END IF;
SET $del_sql = CHAR(10);
SET $del_sql = CONCAT($del_sql,'ALTER TABLE ',$table_name,' DROP PARTITION ', $most_partition_name);
SET @sd_sql = $del_sql;
PREPARE sd FROM @sd_sql;
EXECUTE sd;
DEALLOCATE PREPARE sd;
END LOOP _CLEAR_PARTITION_LOOP_;
END
//
DELIMITER ;
複製代碼
這樣每隔一天運行一次,就能夠自動建立直到後天0點的分區,而且自動刪除過時的分區。
索引