mysql 分区实践之按月份分区,定时增加分区和删除分区

按月份分区,这样再使用分区字段时间来查询数据将会很快,因为这样只需要扫描指定的分区。

1、创建表,并使用RANGE COLUMNS分区。按创建时间create_time字段分区;分区名使用p0、p1、p2、p3 .. 的形式;create_time字段小于2019-01-01的数据将进入p0 分区,依次类推。。

CREATE TABLE "box_fenqu" (
  "id" bigint(36) NOT NULL AUTO_INCREMENT COMMENT '主键',
  "create_by" varchar(50) DEFAULT NULL COMMENT '创建人',
  "create_time" datetime NOT NULL COMMENT '创建日期',
  "update_by" varchar(50) DEFAULT NULL COMMENT '更新人',
  "update_time" datetime DEFAULT NULL COMMENT '更新日期',
  "sys_org_code" varchar(64) DEFAULT NULL COMMENT '所属部门',
  "status" int(10) DEFAULT '0' COMMENT '状态',
  "number" varchar(32) DEFAULT NULL COMMENT '编号',
  "zi_number" varchar(32) DEFAULT NULL COMMENT '自编号',
  "house_address" varchar(32) DEFAULT NULL COMMENT '仓库地址',
  "sb_number" varchar(32) DEFAULT NULL COMMENT '设备id',
  "point_id" varchar(32) DEFAULT NULL COMMENT '投放点id',
  "point" varchar(32) DEFAULT NULL COMMENT '投放点',
  "confirm" int(32) DEFAULT '0' COMMENT '商户/企业用户确认入库,默认为0(未确认)1是已确认',
  "last_point" varchar(32) DEFAULT NULL COMMENT '最近一次投放点名',
  PRIMARY KEY ("id","create_time") USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=2120001 DEFAULT CHARSET=utf8mb4 ROW_FORMAT=DYNAMIC
 PARTITION BY RANGE  COLUMNS(create_time)
(PARTITION p0 VALUES LESS THAN ('2019-01-01') ENGINE = InnoDB,
 PARTITION p1 VALUES LESS THAN ('2019-02-01') ENGINE = InnoDB,
 PARTITION p2 VALUES LESS THAN ('2019-03-01') ENGINE = InnoDB,
 PARTITION p3 VALUES LESS THAN ('2019-04-01') ENGINE = InnoDB,
 PARTITION p4 VALUES LESS THAN ('2019-05-01') ENGINE = InnoDB,
 PARTITION p5 VALUES LESS THAN ('2019-06-01') ENGINE = InnoDB,
 PARTITION p6 VALUES LESS THAN ('2019-07-01') ENGINE = InnoDB,
 PARTITION p7 VALUES LESS THAN ('2019-08-01') ENGINE = InnoDB,
 PARTITION p8 VALUES LESS THAN ('2019-09-01') ENGINE = InnoDB,
 PARTITION p9 VALUES LESS THAN ('2019-10-01') ENGINE = InnoDB,
 PARTITION p10 VALUES LESS THAN ('2019-11-01') ENGINE = InnoDB,
 PARTITION p11 VALUES LESS THAN ('2019-12-01') ENGINE = InnoDB,
 PARTITION p12 VALUES LESS THAN ('2020-01-01') ENGINE = InnoDB,
 PARTITION p13 VALUES LESS THAN ('2020-02-01') ENGINE = InnoDB,
 PARTITION p14 VALUES LESS THAN ('2020-03-01') ENGINE = InnoDB,
 PARTITION p15 VALUES LESS THAN ('2020-04-01') ENGINE = InnoDB,
 PARTITION p16 VALUES LESS THAN ('2020-05-01') ENGINE = InnoDB,
 PARTITION p17 VALUES LESS THAN (MAXVALUE) ENGINE = InnoDB) 

2、创建存储过程proc_create_partition,用它来直接对box_fenqu表进行分区。其实就是将分区名加1,如p2加1就是p3;指定的日期范围就是往后面进一个月,保证最新的分区总是MAXVALUES的 ,如20200401就变成20200401和20200501,其中20200401指定范围,20200501为MAXVALUES

CREATE DEFINER="root"@"%" PROCEDURE "proc_create_partition"(in_tbname VARCHAR(64))
BEGIN

SELECT DATABASE() INTO @dbname;

SET @tbname = in_tbname;

#查询表的最近一次分区的名字,这里按自然数递增的。比如0,1,2,4。去掉 p
SELECT
    REPLACE (partition_name, 'p', '') INTO @PMAX
FROM
    INFORMATION_SCHEMA.PARTITIONS
WHERE
    TABLE_SCHEMA = @dbname
AND table_name = @tbname
ORDER BY
    partition_ordinal_position DESC
LIMIT 1;

#查询表的最近一次分区的指定时间,比如最近时间的分区时 2020.04.01
SELECT
REPLACE(partition_description, '\'', '') INTO @DNAME
FROM
    INFORMATION_SCHEMA.PARTITIONS
WHERE
    TABLE_SCHEMA = @dbname
AND table_name = @tbname
ORDER BY
    partition_ordinal_position DESC
LIMIT 1, 1;


SET @t=CONCAT('alter table `',@dbname,'`.',@tbname,' reorganize partition p',@PMAX,
                          ' into(partition p',@PMAX,' values less than (''',date(DATE_ADD(@DNAME,INTERVAL 1 MONTH)),'''),',
                            'partition p',@PMAX+1,' values less than MAXVALUE)');

SELECT @t;
PREPARE stmt FROM @t;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;

COMMIT;
END

这样传入box_fenqu表名即可进行增加分区了: CALL proc_create_partition('box_fenqu');

3、创建mysql事件,定时调度分区;下面的事件到的意思就是从2020-05-03 00:00:00开始,每过一个月就执行下CALL proc_create_partition('box_fenqu')

CREATE DEFINER=`root`@`%` EVENT `e_create_partition` 
ON SCHEDULE EVERY 1 MONTH STARTS '2020-05-03 00:00:00' 
ON COMPLETION PRESERVE DISABLE 
DO CALL proc_create_partition('box_fenqu')

4、如果有多个表需要都需要定时的动态增加分区的话,就可以再写个存储如下,这个存储过程将对当前库下所有分区表都进行遍历,然后增加分区

CREATE DEFINER="root"@"%" PROCEDURE "proc_create_partition_all"()
BEGIN

  DECLARE tbname varchar(32);
  DECLARE tmpSql varchar(256);
  DECLARE done INT DEFAULT FALSE ;

#查询已手动分区的表
    DECLARE part_cursor CURSOR FOR (SELECT DISTINCT table_name FROM INFORMATION_SCHEMA.PARTITIONS WHERE TABLE_SCHEMA = DATABASE() AND partition_expression IS NOT NULL AND table_name NOT LIKE '%bak');
    DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = TRUE;

#循环对表添加分区
    OPEN part_cursor;
        myLoop: LOOP
            FETCH part_cursor INTO tbname;
            IF done THEN 
                LEAVE myLoop;
            END IF;
            #调用分区存储过程
            CALL proc_create_partition(tbname);

            COMMIT;
        END LOOP myLoop;
    CLOSE part_cursor;
    
END

5、mysql允许创建的分区数量有限,因此我们可以根据业务的情况。定期删除已经不需要的分区

6、查询有数据的最新日期/月份

select REPLACE(partition_description, '\'', '') date from INFORMATION_SCHEMA.PARTITIONS where TABLE_SCHEMA=SCHEMA() AND TABLE_NAME='box_fenqu' AND table_rows != 0
ORDER BY REPLACE(partition_description, '\'', '') desc limit 1

查询有数据的数据条数

select table_rows from INFORMATION_SCHEMA.PARTITIONS where TABLE_SCHEMA=SCHEMA() AND TABLE_NAME='box_fenqu' AND table_rows != 0
ORDER BY REPLACE(partition_description, '\'', '') desc limit 1

7、插入数据测试,编写存储过程如下

CREATE DEFINER="root"@"localhost" PROCEDURE "insert_box_fq"(in max_num int(10),in sDate datetime,in eDate datetime)
begin
 declare i int default 0;
 /*把autocommit设置成0*/
 set autocommit= 0;
 repeat
 set i=i+1;
  INSERT INTO `test`.`box_fenqu`(`create_by`, `create_time`, `update_by`, `update_time`, `sys_org_code`, `status`, `number`, `zi_number`, `house_address`, `sb_number`, `point_id`, `point`, `confirm`, `last_point`) VALUES (rand_string(3), getDateTime(sDate,eDate), rand_string(3), now(), 'A03', 0, rand_string(3), 'A001', '仓库1', rand_string(3), rand_string(3), NULL, 1, rand_string(3));


 until i=max_num end repeat;
commit;
end

插入10万条 call insert_box_fq(100000,'2019-01-01 00:00:00',now())

8、查询各个分区数据情况

SELECT
    PARTITION_NAME AS '分区名',
    TABLE_ROWS AS '记录数' ,
        PARTITION_DESCRIPTION '范围'
FROM
    information_schema.PARTITIONS 
WHERE
    table_schema = 'test' 
    AND table_name = 'box_fenqu';

思考分区结合分页

既然是按日期分区,那么我们在分页查询时就可以使用日期。如下我查询最新的10条数据。效率非常低,因为需要扫描所有的分区。但是事实上我们只需要10条而已,只需要在最新的分区中查找。若最新的分区记录数大于等于10那么就需要前移动一个月份,另外扫描一个分区。依次类推。我想这样应该查询效率很快了吧

SELECT *  FROM box_fenqu ORDER BY create_time DESC LIMIT 10

这样需要写一个存储过程来查询,将limit 扫描的分区只锁定在需要的分区里

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 218,122评论 6 505
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,070评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,491评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,636评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,676评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,541评论 1 305
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,292评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,211评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,655评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,846评论 3 336
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,965评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,684评论 5 347
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,295评论 3 329
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,894评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,012评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,126评论 3 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,914评论 2 355