mysql 自动分区实践

        为提高数据库的查询效率,当数据量大了之后,对表进行分区提高查询效率是必然的,我们可以通过手动对数据库表进行分区,当然为了提高系统稳定性和减少系统维护工作量,使用自动分区是明智之选,以下为我调试了半天才ok的一个关于建立任意数据库的任意表的自动分区的一个存储过程,具体事例如下【传说中的存储过程调试工具dbForge Studio for MySQL真的很垃圾,只能进入存储过程的begin处,F10之后就没法单步调试下一步,直接卡死,最后报错为同步...超时】

 

1、创建测试数据库

create database test;

 

2、创建测试表

create table test_log
(
	created datetime,
	msg varchar(2000)
)

 

3、手动进行分区

      这里要说明的是对自动分区的表必须是在该表有手动分区的前提之下才能进行;

alter table test_log partition by range columns(created)(
	partition p20151001 values less than('2015-10-01 10:10:10'),
	partition p20161001 values less than('2016-10-01 10:10:10'),
	partition p20170210 values less than('2017-02-10 10:10:10')
);

 

 

4、插入数据

insert into test_log values('2015-05-01 01:12:10', 'hi');
insert into test_log values('2016-05-01 01:12:10', 'ni');
insert into test_log values('2016-12-01 01:12:10', 'hao');

 

5、查看数据表的分区

SELECT partition_name, partition_description AS val
FROM INFORMATION_SCHEMA.PARTITIONS
WHERE TABLE_NAME = 'test_log' AND TABLE_SCHEMA = 'test';

其中test_log为表名,test为数据库名

+----------------+-----------------------+
| partition_name | val                   |
+----------------+-----------------------+
| p20151001      | '2015-10-01 10:10:10' |
| p20161001      | '2016-10-01 10:10:10' |
| p20170210      | '2017-02-10 10:10:10' |
+----------------+-----------------------+
3 rows in set (0.00 sec)

 

4、创建存储过程

use test;

DELIMITER ||
-- 删除存储过程
drop procedure if exists imps_create_partition_by_number ||
-- 注意:使用该存储过程必须保证相应数据库表中至少有一个手动分区
-- 创建存储过程[通过数据库名和对应表名]-建多少个分区,分区时间间隔为多少
-- databasename:创建分区的数据库
-- tablename:创建分区的表的名称
-- partition_number:一次创建多少个分区
-- partitiontype:分区类型[0按天分区,1按月分区,2按年分区]
-- gaps:分区间隔,如果分区类型为0则表示每个分区的间隔为 gaps天;
--       如果分区类型为1则表示每个分区的间隔为 gaps月
-- 			 如果分区类型为2则表示每个分区的间隔为 gaps年
create procedure create_partition_by_number (in databasename varchar(50),in tablename varchar(50), in partition_number int, in partitiontype int, in gaps int)
L_END:
begin     
    declare max_partition_description varchar(255) default '';
    declare p_name varchar(255) default 0;       
    declare p_description varchar(255) default 0;   
    declare isexist_partition varchar(255) default 0; 
		declare i int default 1;
	 
		-- 查看对应数据库对应表是否已经有手动分区[自动分区前提是必须有手动分区]
    select partition_name into isexist_partition from information_schema.partitions where table_schema = databasename and table_name = tablename limit 1;
    -- 如果不存在则打印错误并退出存储过程
    if isexist_partition <=> "" then
       select "partition table not is exist" as "ERROR";
       leave L_END;
    end if;
 
    -- 获取最大[降序获取]的分区描述[值]
    select partition_description into max_partition_description  from information_schema.partitions where table_schema = databasename and table_name = tablename order by partition_description desc limit 1;
   
    -- 如果最大分区没有,说明没有手动分区,则无法创建自动分区
    if max_partition_description <=> "" then
       select "partition table is error" as "ERROR";
       leave L_END;
    end if;

    -- 替换前后的单引号[''两个引号表示一个单引号的转义]
    -- set max_partition_description = REPLACE(max_partition_description, '''', '');
		-- 或使用如下语句
		set max_partition_description = REPLACE(max_partition_description, '\'', '');
	
	  -- 自动创建number个分区
    while (i <= partition_number) do

					if (partitiontype = 0) then
						-- 每个分区按天递增,递增gaps天
						set p_description = DATE_ADD(max_partition_description, interval i*gaps day); 
					elseif (partitiontype = 1) then
						-- 每个分区按月递增,递增gaps月
						set p_description = DATE_ADD(max_partition_description, interval i*gaps month); 
					else 
						-- 每个分区按年递增,递增gaps年
						set p_description = DATE_ADD(max_partition_description, interval i*gaps year);
					end if;

					-- 删除空格
					set p_name = REPLACE(p_description, ' ', '');
					-- 如果有横杆替换为空
          set p_name = REPLACE(p_name, '-', '');
					-- 删除时间冒号
					set p_name = REPLACE(p_name, ':', '');

					-- alter table tablename add partition ( partition pname values less than ('2017-02-20 10:05:56') );
          set @sql=CONCAT('ALTER TABLE ', tablename ,' ADD PARTITION ( PARTITION p', p_name ,' VALUES LESS THAN (\'', p_description ,'\'))');
					-- set @sql=CONCAT('ALTER TABLE ', tablename ,' ADD PARTITION ( PARTITION p', p_name ,' VALUES LESS THAN (',p_description,'))');
					-- 打印sql变量
          -- select @sql;
					-- 准备sql语句
          PREPARE stmt from @sql;
					-- 执行sql语句
          EXECUTE stmt;
					-- 释放资源
          DEALLOCATE PREPARE stmt;
					-- 递增变量
          set i = (i + 1) ;

    end while;          
end ||
-- 恢复语句中断符
DELIMITER ;

 

上述两天需要特别注意的地方:
(1)需要替换掉其中的单引号【两个单引号表示一个单引号的转义】

set max_partition_description = REPLACE(max_partition_description, '''', '');

或使用:

 

 set max_partition_description = REPLACE(max_partition_description, '\'', '');


(2)自动添加新分区必须的前提是该表有对应手动分区

 

5、手动调用存储过程

call create_partition_by_number('test', 'test_log', 5, 1, 1);


表示为test数据库的test_log表创建5个分区,分区类型为年,每个分区间隔为1年.
6、使用mysql的事件实现定时分区

-- 开启mysql的事件例程
set global event_scheduler=1;
-- 查看事件是否开启
show variables like '%event_scheduler';
-- 新建一个事件


mysql默认是不会开启事件例程的,需要手动打开,查看是否打开:

mysql> show variables like '%event_scheduler';
+-----------------+-------+
| Variable_name   | Value |
+-----------------+-------+
| event_scheduler | ON    |
+-----------------+-------+
1 row in set (0.00 sec)



7、启用定时器调用创建过程

DELIMITER $$
drop event if exists auto_create_partition_time  $$
create event auto_create_partition_time on schedule every 1 minute
starts sysdate()
do
BEGIN
    call create_partition_by_number('test', 'test_log', 5, 1, 1);
END$$
delimiter ;

每分钟查看分区情况:

8、合并分区

ALTER TABLE test_log reorganize partition 
REORGANIZE PARTITION 
p20151001,p20161001,p20170210 INTO
(PARTITION p20150101 VALUES LESS THAN ('2015-01-01 00:00:00'),
PARTITION p20160101 VALUES LESS THAN ('2016-01-01 00:00:00'),
PARTITION p20170101 VALUES LESS THAN ('2017-01-01 00:00:00'),
);


9、删除表的所有分区

alter table test_log remove partitioning;

 

10、删除分区存储过程创建

/* ------------分区删除存储过程的创建 ----------------*/
-- 替换语句中断符
DELIMITER ||
-- 删除存储过程
drop procedure if exists remove_partition ||

-- 删除分区
-- databasename:创建分区的数据库
-- tablename:创建分区的表的名称
-- dateline: 时间点,早于当前点的分区将会被删除[分区删除同时会删除数据]
-- recordnumber:删除时间点之前的几个分区
create procedure remove_partition(in databasename varchar(50),in tablename varchar(50), in dateline varchar(50), in recordnumber int)
outer_label:
begin     
	declare max_partition_description varchar(255) default 0;
	declare p_description varchar(255) default 0;
	declare p_name varchar(255) default 0; 
	declare i int default 1;
	 
	while i <= recordnumber do
		-- 获取最大[降序获取]的分区描述[值]
		select partition_description into max_partition_description from information_schema.partitions where table_schema = databasename and table_name = tablename
									and STRCMP(REPLACE(partition_description, '''', ''), dateline) < 0  order by partition_description asc limit 1;
		
		if max_partition_description <=> "" then
			leave outer_label; 
		end if;
		
		set max_partition_description = REPLACE(max_partition_description, '''', '');
		set p_description = max_partition_description;

		-- 删除空格
		set p_name = REPLACE(p_description, ' ', '');
		-- 如果有横杆替换为空
		set p_name = REPLACE(p_name, '-', '');
		-- 删除时间冒号
		set p_name = REPLACE(p_name, ':', '');
		-- ALTER TABLE sale_data DROP PARTITION p201010;
	  set @sql=CONCAT('ALTER TABLE ', tablename ,' DROP PARTITION p', p_name);
		-- 打印sql变量
		-- select @sql;
		-- 准备sql语句
		PREPARE stmt from @sql;
		-- 执行sql语句
		EXECUTE stmt;
		-- 释放资源
		DEALLOCATE PREPARE stmt;
		-- 递增变量
		set i = (i + 1) ;
	end while; 	       
end ||
-- 恢复语句中断符
DELIMITER ;


其他语法和实用功能:

alter table test_log add partition (partition p20170220 values less than('2017-02-20'));
alter table test_log drop partition p20170220;

 

 

 

SELECT partition_name,cast(replace(partition_description, ' ', '') AS date) AS val FROM INFORMATION_SCHEMA.PARTITIONS
WHERE TABLE_NAME = 'test' ;
-- 开启mysql的事件例程
set global event_scheduler=1;
-- 查看事件是否开启
show variables like '%event_scheduler';

 

 

 

DELIMITER $$
drop event if exists auto_create_partition_time  $$
-- EVERY 1 day STARTS '2016-05-27 23:59:59'
create event auto_create_partition_time  on schedule every 1 minute
-- 定时器完成后是否继续定时 second秒 minute分 hour day month year
on completion preserve enable
starts sysdate()
do
BEGIN
    call proc_test_log_pt();
END$$
delimiter ;

 

 


-- 删除定时器

drop event if exists test_event;


-- 开启事件

alter event test_event on completion preserve enable;


-- 关闭事件

alter event test_event on completion preserve disable;


-- 查看我的event

select * from mysql.event;

 

快来成为我的朋友或合作伙伴,一起交流,一起进步!
QQ群:961179337
微信:lixiang6153
邮箱:[email protected]
公众号:IT技术快餐
更多资料等你来拿!

 

 

你可能感兴趣的:(mysql)