MySQL数据库实现批量表分区完整示例

2025-05-23 15:50

本文主要是介绍MySQL数据库实现批量表分区完整示例,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

《MySQL数据库实现批量表分区完整示例》通俗地讲表分区是将一大表,根据条件分割成若干个小表,:本文主要介绍MySQL数据库实现批量表分区的相关资料,文中通过代码介绍的非常详细,需要的朋友可以参考...

对于单表大数据量大的问题,如果数据支持分片,使用表分区是个不错的选择,那么mysql是如何实现表分区的?

一、表分区条件

1.数据库存储引擎支持:InnoDB 和 MyISAM引擎

2.数据库版本支持:MySQL 5.1以后(版本不同,具体的特性支持可能会有所不同)

3.数据必须有一个或多个分区键:作为分区的键(字段)必须是主键的一部分(联合主键)

4.分区定义:每个分区必须明确地定义数据范围

5.分区维护:随着时间推移,可能需要添加新的分区或删除旧的分区,以保持数据库的性能和结构

二、常规表和分区表的区别

常规表和分区表对比

常规表分区表
数据结构所有数据存储在单一数据文件数据被逻辑上分成多个部分,可能存放在多个文件甚至多个磁盘
查询优化查询时默认扫描整表数据只访问相关分区数据
I/O操作添加、删除或修改行操作直接作用于整表只对单个分区操作,不影响其他分区数据
备份恢复通常备份整表数据可以单独备份或恢复特定分区
存储管理所有数据集中存储数据分散到多个分区
扩展性随着数据量增长,可能会遇到性能瓶颈更容易水平扩展,可以通过增加新分区来处理更大的数据集,而不需要改变应用程序逻辑
限值和复杂性相对简单,没有特殊的创建或维护要求设计和实现更加复杂,需要考虑如何正确地设置分区策略以满足业务需求

从上面看分区表是否有很大的优势?但是同样分区表也存在一些限值:

分区表的限制

常规表分区表
外键约束×
全文索引

×(5.6以前版本)

⍻(5.6以后版本)

临时表×
列修改×
特定的ALTER TABLE语句×(修改主键、唯一键等)
性能影响数据量影响添加、删除、合并表分区,可能会导致锁表从而影响性能
备份和恢复工具支持通常工具都支持不是所有的备份和恢复工具都完全支持分区表的所有特性
主备服务器数据复制无特殊要求必须保证分区规则一致性,任何不匹配都可能导致复制失败或数据不一致
分区类型限制存储引擎可能限制分区类型
查询优化器的行为简单索引表分区+索引,特情情况的复杂查询可能会有表分区裁剪失效问题

三、表分区的创建

表分区创建关键的三个点:创建表、设置分区键、设置分片策略

示例:

CREATE TABLE sales (
    id INT NOT NULL,
    sale_date DATE NOT NULL,
    amount DECIMAL(10,2)
)
PARTITION BY RANGE (YEAR(sale_date)) (
    PARTITION p0 VALUES LESS THAN (2020),
    PARTITION p1 VALUES LESS THAN (2021),
    PARTITION p2 VALUES LESS THAN (2022),
    PARTITION p3 VALUES LESS THAN MAXVALUE
);

创建表名:sales

分区键:sale_date字段的year()结果,即sale_date字段的年份

分片策略:p0分区存储小于2020年数据、 p1分区存储小于2021年数据、p2分区存储小于2022年数据、p3分区存储其他年份数据(注意:这里的数据“挡板”很重要,设置时一定要小心)

注意:这里的分片策略是“LESS THAN xxx”,表示小于后面策略的数据数据,如上面就是小于指定年份的数据归属于这个分区,因此上面用“数据挡板”这个词

四、将既有表转换分区表脚本

因为表的创建结构不同,因此既有表不能直接转换为分区表,要实现既有表转换为分区表,需要经过以下几步:

1.根据既有表创建同字段结构的新分区表、定义好相关分区策略

2.迁移数据到分区表

3.删除旧表、并将分区表改名为原表

具体实现脚本如下:

CREATE DEFINER=`root`@`%` PRjsOCEDURE `convert_table_to_partition`(IN tbl_name VARCHAR(200),OUT out_status INT)
BEGIN
    DECLARE done INT DEFAULT FALSE;
    -- 输出状态,开始执行状态 100,执行成功状态 200,执行失败状态 50
    SET out_status = 100;
    
    -- 创建一个新的空表,不包含表分区(要转换为分区表,必须是空表)
    SET @create_empty_tbl_sql = CONCAT(
        'CREATE TABLE ', tbl_name, '_partitioned LIKE ', tbl_name, ';'
    );
    PREPARE stmt FROM @create_empty_tbl_sql;
    EXECUTE stmt;
    DEALLOCATE PREPARE stmt;

    -- 获取所有唯一的 year_no 和 month_no 组合作为构建分区定义分区键
    SET @partition_def = '';
    SET @query = CONCAT(
        'SELECT GROUP_CONCAT(
            CONCAT("PARTITION p_", year_no, "_", LPAD(month_no, 2, "0"), 
                   " VALUES LESS THAN (", 
                   CASE WHEN month_no = 12 THEN year_no + 1 ELSE year_no END, ", ", 
                   CASE WHEN month_no = 12 THEN 1 ELSE month_no + 1 END, ")")
            ORDER BY year_no, month_no SEPARATOR ",\n"
        ) INTO @partition_def
        FROM (
            SELECT DISTINCT year_no, month_no
            FROM ', tbl_name, '
            ORDER BY year_no, month_no
        ) AS unique_years_months;'
    );
    PREPARE stmt FROM @query;
    EXECUTE stmt;
    DEALLOCATE PREPARE stmt;

    -- 调试信息:输出分区定义字符串
    --SELECT tbl_name,@partition_def;

    -- 检查是否有有效的分区定义
    IF @partition_def IS NULL OR @partition_def = '' THEN
        SELECT tbl_name,'No data found for partitioning. Skipping partition creation and data migration.';
        -- 空表则直接添加 p_max 分区用于捕获未来数据
        SET @partition_def = '\nPARTITION p_max VALUES LESS THAN (MAXVALUE, MAXVALUE)';
    ELSE
        -- 添加 p_max 分区用于捕获未来数据
     http://www.chinasem.cn   SET @partition_def = CONCAT(@partition_def, ',\nPARTITION p_max VALUES LESS THAN (MAXVALUE, MAXVALUE)');
    END IF;

    -- 使用 ALTER TABLE 添加分区定义
    SET @add_partitions_sql = CONCAT(
        'ALTER TABLE ', tbl_name, '_partitioned 
        PARTITION BY RANGE COLUMNS(year_no, month_no) (', @partition_def, ');'
    );

    -- 调试信息:输出添加分区的 SQL 语句
    --SELECT tbl_name,@add_partitions_sql;

    PREPARE stmt FROM @add_partitions_sql;
    EXECUTE stmt;
    DEALLOCATE PREPARE stmt;

    -- 迁移数据到新的分区表
    SET @insert_into_partitioned_sql = CONCAT(
        'INSERT INTO ', tbl_name, '_partitioned SELECT * FROM ', tbl_name, ';'
    );

    -- 调试信息:输出插入数据的 SQL 语句
    -- SELECT tbl_name,@insert_into_partitioned_sql;

    PREPARE stmt FROM @insert_into_partitioned_sql;
    EXECUTE stmt;
    DEALLOCATE PREPARE stmt;

    -- 验证数据迁移是否成功
    SET @count_original = CONCAT('SELECT COUNT(*) INTO @count_original FROM ', tbl_name);
    PREPARE stmt FROM @count_original;
    EXECUTE stmt;
    DEALLOCATE PREPARE stmt;

    SET @count_partitioned = CONCAT('SELECT COUNT(*) INTO @count_partitioned FROM ', tbl_name, '_partitioned');
    PREPARE stmt FROM @count_partitioned;
    EXECUTE stmt;
    DEALLOCATE PREPARE stmt;

    -- 比较原表和新分区表的数据行数
    -- SELECT tbl_name,@count_original, @count_partitioned;

    
    -- 如果数据迁移成功,删除旧表并重命名新表(无论是否有数据,均删除缓存表)
    IF @count_original = @count_partitioned THEN
        -- 删除旧表
        SET @drop_old_table_sql = CONCAT('DROP TABLE IF EXISTS ', tbl_name);
        PREPARE stmt FROM @drop_old_table_sql;
        EXECUTE stmt;
        DEALLOCATE PREPARE stmt;

        -- 重命名新表为旧表名
        SET @rename_tables_sql = CONCAT('RENAME TABLE ', tbl_name, '_partitioned TO ', tbl_name);
        PREPARE stmt F编程ROM @rename_tables_sql;
        EXECUTE stmt;
        DEALLOCATE PREPARE stmt;

        -- SELECT tbl_name,'Table conversion and data migration completed successfully.';
        SET out_status = 200;
    ELSE
        -- SELECT tbl_name,'Data migration failed, check the logs for more information.';
        SET out_status = 50;
    END IF;
    
END

上面脚本是一个完整的将既有表转换为以“year_no”和“month_no”字段为分区键的分区表,主要有以下几步操作:

1)以既有表为模板创建一个新的空表,不包含表分区(要转换为分区表,必须是空表)

2)获取所有唯一的 year_no 和 month_no 组合并构建分区定义字符串(对既有数据分析需要划分的分区策略)

3)检查是否有效的分区定义,若无分区定义,强烈建议则创建一个默认的分区策略p_max以存储未来的数据

4)更新空表,添加相关的分区策略

5)迁移历史数据到分区表

6)数据迁移校验(验证数据完整性)

7)删除旧表(回收表名)

8)将新分区表改名为原表名

五、批量转换表为分区表

批量将常规表转换python为分区表,具体脚本如下:

CREATE DEFINER=`root`@`%` PROCEDURE `tables_convert_to_partition`()
BEGIN
    DECLARE done INT DEFAULT FALSE;
    DECLARE tbl_name VARCHAR(64);
    DECLARE convert_status INT;
    DECLARE cur CURSOR FOR
        SELECT TABLE_NAME
        FROM information_schema.TABLES
        WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME LIKE 'ai_result_%';
    DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = TRUE;

    OPEN cur;

    read_loop: LOOP
        FETCH cur INTO tbl_name;
        IF done THEN
            LEAVE read_loop;
        END IF;
      
        -- 调试信息:输出正在转换的表
        SELECT tbl_name,'covering...';

        CALL convert_table_to_partition(tbl_name,@status);
        
        SET convert_status = @status;
        
        -- 根据返回的状态进行相应的处理
        CASE convert_status
            WHEN 100 THEN
                -- 开始状态,可以忽略,因为这是预期的初始状态
                SELECT tbl_name, 'Started conversion.';
            WHEN 200 THEN
                -- 成功完成
                SELECT tbl_name, 'Conversion and data migration completed successfully.';
            WHEN 50 THEN
                -- 失败
                SELECT tbl_name, 'Data migration failed. Check the logs for more information.';
            ELSE
                -- 未知状态
                SELECT tbl_name, CONCAT('Unknown status: ', status);
        END CASE;
    END LOOP;

    CLOSE cur;
END

这里是以“ai_result_”开头的表为例,将所有相关表转换为分区表,在执行这个存储过程时,操作用户必须要有information_schema数据库读取权限,这样才能查询出相关的表名从而进行转换。

该脚本建议为一次性执行脚本,避免对标频繁转换,防止锁表(因此表名前缀已固定在代码中,需根据自身需求修改)

六、表分区维护:添加表分区

表分区经过上面的过程创建,理论上已经对历史数据进行表分区,对未来数据也能存储到p_max分区,但是p_max分区数据如果不进行维护,同样会有数据量过大问题,因此我们需要定期切割p_max分区并增加相关表分区,这个操作需要在数据进入之前执行,具体执行脚本如下:

CREATE DEFINER=`root`@`%` PROCEDURE `add_monthly_partitions`(IN tbl_name VARCHAR(64), IN year_no INT, IN month_no INT,OUT out_status INT)
BEGIN
    DECLARE done INT DEFAULT FALSE;
    
    -- 输出状态,开始执行状态 100,执行成功状态 200,执行失败状态 50
    SET out_status = 100;
    
    -- 检查待添加的分区是否已经存在
    SET @partition_exists = EXISTS (SELECT 1 FROM information_schema.PARTITIONS 
                                    WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = tbl_name 
                                      AND PARTITION_NAME = CONCAT('p_', year_no, '_', LPAD(month_no, 2, '0')));
                                      
    IF @partition_exists THEN
        -- 如果分区已存在,直接返回消息
        -- SELECT CONCAT('Partition p_', year_no, '_', LPAD(month_no, 2, '0'), ' already exists. No action taken.') AS message;
        SET out_status = 200;
    ELSE
      -- 检查表中是否已经存在 p_max 分区
      SET @has_p_max = EXISTS (SELECT 1 FROM information_schema.PARTITIONS 
                               WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = tbl_name 
                                 AND PARTITION_NAME = 'p_max');

      -- 构建添加分区的 SQL 语句
      IF @has_p_max THEN
          -- 如果存在 p_max 分区,则重新组织分区,将 p_max 分割成新分区和更新后的 p_max
          SET @reorganize_partition_sql = CONCAT(
              'ALTER TABLE ', tbl_name, ' REORGANIZE PARTITION p_max INTO (
                  PARTITION p_'beeIChS, year_no, '_', LPAD(month_no, 2, '0'), 
                  ' VALUES LESS THAN (', 
                  CASE WHEN month_no = 12 THEN year_no + 1 ELSE year_no END, ', ', 
                  CASE WHEN month_no = 12 THEN 1 ELSE month_no + 1 END, '),
                  PARTITION p_max VALUES LESS THAN (MAXVALUE, MAXVALUE)
              )'
          );
          PREPARE stmt FROM @reorganize_partition_sql;
          EXECUTE stmt;
          DEALLOCATE PREPARE stmt;

          -- SELECT CONCAT('Partition p_', year_no, '_', LPAD(month_no, 2, '0'), ' and updated p_max added successfully.') AS message;
          SET out_status = 200;
      ELSE
          -- 如果不存在 p_max 分区,则直接添加新分区
          SET @add_partition_sql = CONCAT(
              'ALTER TABLE ', tbl_name, ' ADD PARTITION (
                  PARTITION p_', year_no, '_', LPAD(month_no, 2, '0'), 
                  ' VALUES LESS THAN (', 
                  CASE WHEN month_no = 12 THEN year_no + 1 ELSE year_no END, ', ', 
                  CASE WHEN month_no = 12 THEN 1 ELSE month_no + 1 END, ')
              )'
          );
          PREPARE stmt FROM @add_partition_sql;
          EXECUTE stmt;
          DEALLOCATE PREPARE stmt;
          
          -- SELECT CONCAT('Partition p_', year_no, '_', LPAD(month_no, 2, '0'), ' added successfully.') AS message;
          SET out_status = 200;
      END IF;
    END IF;
END

 上面脚本的执行过程如下:

1)检测待添加的分区是否已存在(已存在则不添加,不存在才添加)

2)检测表中是否存在p_max 分区(检测待切割分区,若存在则切割分区,若不存在这创建分区)

3)切割p_max分区为新分区和新的p_max分区(此处会调整p_max分区的分片策略)

七、批量维护:批量添加表分区

 批量给相关表添加表分区,具体脚本如下:

CREATE DEFINER=`root`@`%` PROCEDURE `tables_add_monthly_partition`(IN tbl_prefix VARCHAR(64), IN year_no INT, IN month_no INT,OUT out_status INT)
BEGIN
    DECLARE done INT DEFAULT FALSE;
    DECLARE tbl_name VARCHAR(64);
    DECLARE cur CURSOR FOR
        SELECT TABLE_NAME
        FROM information_schema.TABLES
        WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME LIKE CONCAT(tbl_prefix, '%');
    DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = TRUE;

    -- 输出状态,开始执行状态 100,执行成功状态 200,执行失败状态 50
    SET out_status = 100;

    -- 打开游标
    OPEN cur;

    read_loop: LOOP
        FETCH cur INTO tbl_name;
        IF done THEN
            LEAVE read_loop;
        END IF;

        -- 检查表是否已经是分区表
        SET @is_partitioned = EXISTS (SELECT 1 FROM information_schema.PARTITIONS 
                                      WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = tbl_name);

        IF NOT @is_partitioned THEN
            -- 如果表不是分区表,先调用 convert_table_to_partition 进行转换
            CALL convert_table_to_partition(tbl_name,@status);
            
            -- 转换后再次检查是否成功转换为分区表
            SET @is_partitioned = EXISTS (SELECT 1 FROM information_schema.PARTITIONS 
                                          WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = tbl_name);
            
            IF NOT @is_partitioned THEN
                -- 如果转换失败,跳过后续操作并输出错误信息
                SELECT CONCAT('Failed to convert table ', tbl_name, ' to partitioned. Skipping.') AS message;
                SET out_status = 50;
                ITERATE read_loop;
            END IF;
        END IF;

        -- 调用 add_monthly_partitions 为当前表添加分区
        CALL add_monthly_partitions(tbl_name, year_no, month_no,@status);

        -- 可选:输出操作结果(用于调试)
        -- SELECT CONCAT('Processed table: ', tbl_name) AS status;
    END LOOP;

    -- 关闭游标
    CLOSE cur;

    -- 输出完成信息
    -- SELECT CONCAT('BATch partition addition completed for tables with prefix "', tbl_prefix, '".') AS message;
    SET out_status = 200;
END

 批量添加表分区需要传入相关表前缀,如上面示例中的“ai_result_”,此脚本会将非分区表转换为分区表,再给分区表添加相应的表分区,具体执行过程如下:

1)获取所有相关表

2)遍历判断表是否是分区表

3)非分区表被转换为分区表

4)给分区表添加表分区策略

该脚本请慎重执行,上面我们有常规表和分区表的对比,执行脚本很简单(批量自动完成),但执行的后果请慎重考虑!

总结 

到此这篇关于MySQL数据库实现批量表分区的文章就介绍到这了,更多相关MySQL批量表分区内容请搜索China编程(www.chinasem.cn)以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程China编程(www.chinasem.cn)!

这篇关于MySQL数据库实现批量表分区完整示例的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1154746

相关文章

linux批量替换文件内容的实现方式

《linux批量替换文件内容的实现方式》本文总结了Linux中批量替换文件内容的几种方法,包括使用sed替换文件夹内所有文件、单个文件内容及逐行字符串,强调使用反引号和绝对路径,并分享个人经验供参考... 目录一、linux批量替换文件内容 二、替换文件内所有匹配的字符串 三、替换每一行中全部str1为st

SpringBoot集成MyBatis实现SQL拦截器的实战指南

《SpringBoot集成MyBatis实现SQL拦截器的实战指南》这篇文章主要为大家详细介绍了SpringBoot集成MyBatis实现SQL拦截器的相关知识,文中的示例代码讲解详细,有需要的小伙伴... 目录一、为什么需要SQL拦截器?二、MyBATis拦截器基础2.1 核心接口:Interceptor

SpringBoot集成EasyPoi实现Excel模板导出成PDF文件

《SpringBoot集成EasyPoi实现Excel模板导出成PDF文件》在日常工作中,我们经常需要将数据导出成Excel表格或PDF文件,本文将介绍如何在SpringBoot项目中集成EasyPo... 目录前言摘要简介源代码解析应用场景案例优缺点分析类代码方法介绍测试用例小结前言在日常工作中,我们经

基于Python实现简易视频剪辑工具

《基于Python实现简易视频剪辑工具》这篇文章主要为大家详细介绍了如何用Python打造一个功能完备的简易视频剪辑工具,包括视频文件导入与格式转换,基础剪辑操作,音频处理等功能,感兴趣的小伙伴可以了... 目录一、技术选型与环境搭建二、核心功能模块实现1. 视频基础操作2. 音频处理3. 特效与转场三、高

Python实现中文文本处理与分析程序的示例详解

《Python实现中文文本处理与分析程序的示例详解》在当今信息爆炸的时代,文本数据的处理与分析成为了数据科学领域的重要课题,本文将使用Python开发一款基于Python的中文文本处理与分析程序,希望... 目录一、程序概述二、主要功能解析2.1 文件操作2.2 基础分析2.3 高级分析2.4 可视化2.5

Java实现预览与打印功能详解

《Java实现预览与打印功能详解》在Java中,打印功能主要依赖java.awt.print包,该包提供了与打印相关的一些关键类,比如PrinterJob和PageFormat,它们构成... 目录Java 打印系统概述打印预览与设置使用 PageFormat 和 PrinterJob 类设置页面格式与纸张

使用Go实现文件复制的完整流程

《使用Go实现文件复制的完整流程》本案例将实现一个实用的文件操作工具:将一个文件的内容完整复制到另一个文件中,这是文件处理中的常见任务,比如配置文件备份、日志迁移、用户上传文件转存等,文中通过代码示例... 目录案例说明涉及China编程知识点示例代码代码解析示例运行练习扩展小结案例说明我们将通过标准库 os

MySQL 8 中的一个强大功能 JSON_TABLE示例详解

《MySQL8中的一个强大功能JSON_TABLE示例详解》JSON_TABLE是MySQL8中引入的一个强大功能,它允许用户将JSON数据转换为关系表格式,从而可以更方便地在SQL查询中处理J... 目录基本语法示例示例查询解释应用场景不适用场景1. ‌jsON 数据结构过于复杂或动态变化‌2. ‌性能要

Python实现终端清屏的几种方式详解

《Python实现终端清屏的几种方式详解》在使用Python进行终端交互式编程时,我们经常需要清空当前终端屏幕的内容,本文为大家整理了几种常见的实现方法,有需要的小伙伴可以参考下... 目录方法一:使用 `os` 模块调用系统命令方法二:使用 `subprocess` 模块执行命令方法三:打印多个换行符模拟

SpringBoot+EasyPOI轻松实现Excel和Word导出PDF

《SpringBoot+EasyPOI轻松实现Excel和Word导出PDF》在企业级开发中,将Excel和Word文档导出为PDF是常见需求,本文将结合​​EasyPOI和​​Aspose系列工具实... 目录一、环境准备与依赖配置1.1 方案选型1.2 依赖配置(商业库方案)二、Excel 导出 PDF