Andy Niu �����ĵ�

Andy Niu

Andy Niu Help  1.0.0.0
Mysql分区表

变量

 创建分区表
 
 动态管理分区
 

详细描述

变量说明

创建分区表
1、考虑下面的需求,alarmlog记录告警日志信息,用户查询的时候,查询条件都带着时间段。
    alarmlog的数据量很大,如果都写到一个表里面,会导致查询效率很差。
2、应用程序控制,每天建立一个表。但是这样导致一个问题,客户端查询的时候,只能查询一天的告警,
    如果客户端查询的时间段跨多天,应用程序就需要找出对应的表,分别查询。
3、有没有更好的办法?
    使用分区表,逻辑上是一个表,物理上是多个表组成。
4、创建表的时候,创建分区,如下:
    create table alarmlog(ID int, MSG varchar(32), TIME datetime)
    engine=innodb 
    partition by range(to_days(TIME))
    (
        partition p1 values less than (to_days('2015-12-20')),
        partition p2 values less than (to_days('2015-12-21')),
        partition p3 values less than (to_days('2015-12-22')),
        partition p4 values less than (to_days('2015-12-23')),
        partition p5 values less than (to_days('2015-12-24')),
        partition p6 values less than (to_days('2015-12-25')),
        partition p7 values less than (to_days('2015-12-26'))
    );
5、添加测试数据
    BEGIN
        declare vI int default 0;
        declare vRand int default 0;
    
        start transaction;
        while (vI<=100) do 
            set vI = vI +1;
            set vRand=floor(rand()*6);
            insert into alarmlog(id,msg,time) value(vI,concat('alarm',vI),date_add(now(),interval vRand day));      
        end while;
        commit;
    END
6、查看执行计划
    mysql> desc partitions select * from alarmlog where time <'2015-12-20';
    +----+-------------+----------+------------+------+---------------+------+---------+------+------+-------------+
    | id | select_type | table    | partitions | type | possible_keys | key  | key_len | ref  | rows | Extra       |
    +----+-------------+----------+------------+------+---------------+------+---------+------+------+-------------+
    |  1 | SIMPLE      | alarmlog | p1         | ALL  | NULL          | NULL | NULL    | NULL |    9 | Using where |
    +----+-------------+----------+------------+------+---------------+------+---------+------+------+-------------+
    1 row in set
    
    mysql> desc partitions select * from alarmlog where time <'2015-12-21';
    +----+-------------+----------+------------+------+---------------+------+---------+------+------+-------------+
    | id | select_type | table    | partitions | type | possible_keys | key  | key_len | ref  | rows | Extra       |
    +----+-------------+----------+------------+------+---------------+------+---------+------+------+-------------+
    |  1 | SIMPLE      | alarmlog | p1,p2      | ALL  | NULL          | NULL | NULL    | NULL |   30 | Using where |
    +----+-------------+----------+------------+------+---------------+------+---------+------+------+-------------+
    1 row in set
    可以看到mysql直接定位到分区表,进行扫描,缩小了扫描的范围,提高效率
7、删除分区
    alter table alarmlog drop partition p1;
8、添加分区
    alter table alarmlog add partition (partition p8 values less than (to_days('2015-12-27')));
    注意:对表增加一个分区,表之前必须创建过分区。
9、随着时间的过去,运行到 2015-12-27,考虑插入一条告警,如下:
    mysql> insert into alarmlog(id,msg,time) value(1001,concat('alarm',1001),'2015-12-27');
    1526 - Table has no partition for value 736324
    也就是说,没有分区存放这条记录。
    因此,需要动态增加分区。同时,很早以前的分区,数据不再使用,需要删除分区。
10、动态管理分区,删除老的分区,增加新的分区。使用mysql的事件调度器。
动态管理分区
1、python生成分区表,如下:
    #coding=gb18030
    from datetime import *
    class GeneMysqlPartition:
        def Gene(self):        
            pFile = open('MysqlPartition.txt','w')
            i=0
            curDate=date.today()-timedelta(days=10)
            while(i<10):
                ss= "partition p%04d%02d%02d values less than (to_days('%s-%s-%s')),\n" %(curDate.year,curDate.month,curDate.day,curDate.year,curDate.month,curDate.day)
                pFile.write(ss)
                i=i+1
                curDate=curDate+timedelta(days=1)
            pFile.close()
    
    if __name__ == '__main__':
        geneA = GeneMysqlPartition();
        geneA.Gene();
2、创建表,如下:
    use niu_test;
    create table alarm_log(ID int, MSG varchar(32), TIME datetime)
    engine=innodb 
    partition by range(to_days(TIME))
    (
        partition p20151220 values less than (to_days('2015-12-20'))
    );
3、动态管理分区表
    DROP PROCEDURE IF EXISTS `exe_dynamic_sql`;
    DELIMITER ;;
    CREATE DEFINER=`root`@`%` PROCEDURE `exe_dynamic_sql`(iDynamicSql TEXT)
    BEGIN
        set @exeSql = iDynamicSql;
        prepare stmt from @exeSql;
        execute stmt;
        deallocate prepare stmt; -- 释放掉预处理段
    END
    ;;
    DELIMITER ;
    
    
    DROP PROCEDURE IF EXISTS `manage_partitions`;
    DELIMITER ;;
    CREATE DEFINER=`root`@`%` PROCEDURE `manage_partitions`(iDataBaseName varchar(100), iTableName varchar(100), iOldPartitionSaveDays int, iPartitionPreserverDays int, iPartitionIntervalDays int)
    BEGIN
        declare vPartitionName varchar(32) default '';
        declare vIsOver int default 0;
        declare vCurDate date default curdate();
        declare vOldDate date default curdate();
        declare vLastDate date default curdate();
        declare vExpectDate date default curdate();
        declare vLastPartitionName varchar(32) default '';
    
        declare vDynamicSql text default '';
    
        declare vCursorPartitionName cursor for select partition_name from information_schema.partitions where table_schema=iDataBaseName and table_name=iTableName;
        declare continue handler for sqlstate '02000' set vIsOver = 1;
    
        -- delete old partition
        open vCursorPartitionName;
        fetch vCursorPartitionName into vPartitionName;
    
        while(vIsOver = 0) do 
            set vOldDate =  substr(vPartitionName,2);
            -- select datediff(vCurDate,vOldDate);
            if(datediff(vCurDate,vOldDate)> iOldPartitionSaveDays) then 
                set vDynamicSql = concat('alter table ',iTableName,' drop partition ',vPartitionName);
                -- select vDynamicSql;
                call exe_dynamic_sql(vDynamicSql);
            end if;
            fetch vCursorPartitionName into vPartitionName;
        end while;
    
        close vCursorPartitionName;
        -- delete old partition
        
    
        -- add new partition
        set vLastPartitionName = (select max(partition_name) from information_schema.partitions where table_schema=iDataBaseName and table_name=iTableName);
        set vLastDate =  substr(vLastPartitionName,2);
        select date_add(curdate(),interval iPartitionPreserverDays day) into vExpectDate;
    
        while(vLastDate < vExpectDate) do 
            set vLastDate = vLastDate + interval iPartitionIntervalDays day;
            set vDynamicSql = concat('alter table ',iTableName,' add partition (partition p',date_format(vLastDate,'%Y'),date_format(vLastDate,'%m'),date_format(vLastDate,'%d'),' values less than (to_days(\'', vLastDate,'\')))');
            -- select vDynamicSql;
            call exe_dynamic_sql(vDynamicSql);
        end while;
        -- add new partition
    
    END
    ;;
    DELIMITER ;
4、计划任务
    DROP EVENT IF EXISTS `event_day`;
    DELIMITER ;;
    CREATE EVENT `event_day` ON SCHEDULE EVERY 1 DAY STARTS '2015-06-20 08:05:00' ENDS '2025-06-20 08:00:00' ON COMPLETION PRESERVE ENABLE DO begin 
    call manage_partitions('niu_test','alarm_log',365*2,30,7);
    call manage_partitions('niu_test','realdata_log',365*2,30,7);
    end
    ;;
    DELIMITER ;
Copyright (c) 2015~2016, Andy Niu @All rights reserved. By Andy Niu Edit.