-- ======================================== -- GPS分段里程表分区优化方案 -- ======================================== -- 功能说明: -- 1. 将 tb_vehicle_gps_segment_mileage 表按月进行分区 -- 2. 提高大数据量下的查询性能 -- 3. 方便历史数据归档和删除 -- -- 注意事项: -- 1. 执行此脚本前请备份数据库! -- 2. 数据量大时转换过程较慢,建议在业务低峰期执行 -- 3. 分区后主键和唯一键必须包含分区键(segment_start_time) -- ======================================== -- 步骤1:备份原表数据 -- 建议先手动执行:mysqldump -u用户名 -p 数据库名 tb_vehicle_gps_segment_mileage > backup_segment_mileage.sql -- 步骤2:创建新的分区表 CREATE TABLE `tb_vehicle_gps_segment_mileage_new` ( `segment_id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '分段ID', `vehicle_id` bigint(20) NOT NULL COMMENT '车辆ID', `vehicle_no` varchar(20) DEFAULT NULL COMMENT '车牌号', `segment_start_time` datetime NOT NULL COMMENT '时间段开始时间', `segment_end_time` datetime NOT NULL COMMENT '时间段结束时间', `start_longitude` decimal(10,7) DEFAULT NULL COMMENT '起点经度', `start_latitude` decimal(10,7) DEFAULT NULL COMMENT '起点纬度', `end_longitude` decimal(10,7) DEFAULT NULL COMMENT '终点经度', `end_latitude` decimal(10,7) DEFAULT NULL COMMENT '终点纬度', `segment_distance` decimal(10,3) DEFAULT 0.000 COMMENT '段距离(公里)', `gps_point_count` int(11) DEFAULT 0 COMMENT 'GPS点数量', `gps_ids` text COMMENT '关联的GPS记录ID列表(逗号分隔)', `task_id` bigint(20) DEFAULT NULL COMMENT '关联任务ID', `task_code` varchar(50) DEFAULT NULL COMMENT '任务编号', `calculate_method` varchar(20) DEFAULT 'tianditu' COMMENT '计算方式(tianditu-天地图/haversine-球面距离)', `create_time` datetime DEFAULT NULL COMMENT '创建时间', `update_time` datetime DEFAULT NULL COMMENT '更新时间', PRIMARY KEY (`segment_id`, `segment_start_time`), -- 注意:分区表的唯一键必须包含分区键 UNIQUE KEY `uk_vehicle_time` (`vehicle_id`, `segment_start_time`), KEY `idx_vehicle_id` (`vehicle_id`), KEY `idx_start_time` (`segment_start_time`), KEY `idx_task_id` (`task_id`), KEY `idx_vehicle_task` (`vehicle_id`, `task_id`), KEY `idx_vehicle_date` (`vehicle_id`, `segment_start_time`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='车辆GPS分段里程表(分区版)' PARTITION BY RANGE (TO_DAYS(segment_start_time)) ( -- 2024年分区 PARTITION p202401 VALUES LESS THAN (TO_DAYS('2024-02-01')), PARTITION p202402 VALUES LESS THAN (TO_DAYS('2024-03-01')), PARTITION p202403 VALUES LESS THAN (TO_DAYS('2024-04-01')), PARTITION p202404 VALUES LESS THAN (TO_DAYS('2024-05-01')), PARTITION p202405 VALUES LESS THAN (TO_DAYS('2024-06-01')), PARTITION p202406 VALUES LESS THAN (TO_DAYS('2024-07-01')), PARTITION p202407 VALUES LESS THAN (TO_DAYS('2024-08-01')), PARTITION p202408 VALUES LESS THAN (TO_DAYS('2024-09-01')), PARTITION p202409 VALUES LESS THAN (TO_DAYS('2024-10-01')), PARTITION p202410 VALUES LESS THAN (TO_DAYS('2024-11-01')), PARTITION p202411 VALUES LESS THAN (TO_DAYS('2024-12-01')), PARTITION p202412 VALUES LESS THAN (TO_DAYS('2025-01-01')), -- 2025年分区 PARTITION p202501 VALUES LESS THAN (TO_DAYS('2025-02-01')), PARTITION p202502 VALUES LESS THAN (TO_DAYS('2025-03-01')), PARTITION p202503 VALUES LESS THAN (TO_DAYS('2025-04-01')), PARTITION p202504 VALUES LESS THAN (TO_DAYS('2025-05-01')), PARTITION p202505 VALUES LESS THAN (TO_DAYS('2025-06-01')), PARTITION p202506 VALUES LESS THAN (TO_DAYS('2025-07-01')), PARTITION p202507 VALUES LESS THAN (TO_DAYS('2025-08-01')), PARTITION p202508 VALUES LESS THAN (TO_DAYS('2025-09-01')), PARTITION p202509 VALUES LESS THAN (TO_DAYS('2025-10-01')), PARTITION p202510 VALUES LESS THAN (TO_DAYS('2025-11-01')), PARTITION p202511 VALUES LESS THAN (TO_DAYS('2025-12-01')), PARTITION p202512 VALUES LESS THAN (TO_DAYS('2026-01-01')), -- 2026年分区 PARTITION p202601 VALUES LESS THAN (TO_DAYS('2026-02-01')), PARTITION p202602 VALUES LESS THAN (TO_DAYS('2026-03-01')), PARTITION p202603 VALUES LESS THAN (TO_DAYS('2026-04-01')), PARTITION p202604 VALUES LESS THAN (TO_DAYS('2026-05-01')), PARTITION p202605 VALUES LESS THAN (TO_DAYS('2026-06-01')), PARTITION p202606 VALUES LESS THAN (TO_DAYS('2026-07-01')), PARTITION p202607 VALUES LESS THAN (TO_DAYS('2026-08-01')), PARTITION p202608 VALUES LESS THAN (TO_DAYS('2026-09-01')), PARTITION p202609 VALUES LESS THAN (TO_DAYS('2026-10-01')), PARTITION p202610 VALUES LESS THAN (TO_DAYS('2026-11-01')), PARTITION p202611 VALUES LESS THAN (TO_DAYS('2026-12-01')), PARTITION p202612 VALUES LESS THAN (TO_DAYS('2027-01-01')), -- 未来数据分区(可以容纳2027年及以后的数据) PARTITION pfuture VALUES LESS THAN MAXVALUE ); -- 步骤3:迁移数据到新表 -- 方式1:一次性迁移(适用于数据量较小,如100万以下) -- INSERT INTO tb_vehicle_gps_segment_mileage_new SELECT * FROM tb_vehicle_gps_segment_mileage; -- 方式2:分批迁移(适用于大数据量,推荐) -- 按月份分批迁移,减少锁表时间 -- 2024年1月 INSERT INTO tb_vehicle_gps_segment_mileage_new SELECT * FROM tb_vehicle_gps_segment_mileage WHERE segment_start_time >= '2024-01-01' AND segment_start_time < '2024-02-01'; -- 2024年2月 INSERT INTO tb_vehicle_gps_segment_mileage_new SELECT * FROM tb_vehicle_gps_segment_mileage WHERE segment_start_time >= '2024-02-01' AND segment_start_time < '2024-03-01'; -- 继续按月迁移...(根据实际数据情况调整) -- 2024年3月至12月 INSERT INTO tb_vehicle_gps_segment_mileage_new SELECT * FROM tb_vehicle_gps_segment_mileage WHERE segment_start_time >= '2024-03-01' AND segment_start_time < '2025-01-01'; -- 2025年数据 INSERT INTO tb_vehicle_gps_segment_mileage_new SELECT * FROM tb_vehicle_gps_segment_mileage WHERE segment_start_time >= '2025-01-01' AND segment_start_time < '2026-01-01'; -- 2026年数据 INSERT INTO tb_vehicle_gps_segment_mileage_new SELECT * FROM tb_vehicle_gps_segment_mileage WHERE segment_start_time >= '2026-01-01'; -- 步骤4:验证数据一致性 -- 检查原表和新表的记录数 SELECT 'Original Table' as table_name, COUNT(*) as record_count FROM tb_vehicle_gps_segment_mileage UNION ALL SELECT 'New Table' as table_name, COUNT(*) as record_count FROM tb_vehicle_gps_segment_mileage_new; -- 检查各分区的数据量 SELECT PARTITION_NAME, TABLE_ROWS, AVG_ROW_LENGTH, DATA_LENGTH, INDEX_LENGTH, CREATE_TIME FROM information_schema.PARTITIONS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'tb_vehicle_gps_segment_mileage_new' ORDER BY PARTITION_NAME; -- 步骤5:切换表名(谨慎操作!) -- 建议在业务低峰期执行,整个操作应在事务中完成 -- START TRANSACTION; -- 重命名原表为备份表 RENAME TABLE tb_vehicle_gps_segment_mileage TO tb_vehicle_gps_segment_mileage_backup; -- 将新表重命名为正式表 RENAME TABLE tb_vehicle_gps_segment_mileage_new TO tb_vehicle_gps_segment_mileage; -- 如果一切正常,提交事务 -- COMMIT; -- 如果出现问题,回滚 -- ROLLBACK; -- 步骤6:验证应用正常运行 -- 请在应用层测试以下功能: -- 1. GPS里程计算功能 -- 2. 车辆里程查询 -- 3. 任务里程统计 -- 4. 相关报表功能 -- 步骤7:确认无误后删除备份表(可选,建议保留一段时间) -- DROP TABLE tb_vehicle_gps_segment_mileage_backup; -- ======================================== -- 分区维护操作(定期执行) -- ======================================== -- 添加新月份的分区(每月或每季度执行一次) -- 例如:添加2027年1月的分区 -- ALTER TABLE tb_vehicle_gps_segment_mileage -- REORGANIZE PARTITION pfuture INTO ( -- PARTITION p202701 VALUES LESS THAN (TO_DAYS('2027-02-01')), -- PARTITION pfuture VALUES LESS THAN MAXVALUE -- ); -- 删除历史分区(归档旧数据,释放空间) -- 例如:删除2024年1月的数据(删除前请确保已备份) -- ALTER TABLE tb_vehicle_gps_segment_mileage DROP PARTITION p202401; -- 或者清空分区数据但保留分区结构 -- ALTER TABLE tb_vehicle_gps_segment_mileage TRUNCATE PARTITION p202401; -- ======================================== -- 性能优化建议 -- ======================================== -- 1. 查询时尽量带上时间范围条件,以充分利用分区裁剪 -- 示例: -- SELECT * FROM tb_vehicle_gps_segment_mileage -- WHERE segment_start_time >= '2025-01-01' -- AND segment_start_time < '2025-02-01' -- AND vehicle_id = 123; -- 2. 定期分析表以优化查询计划 -- ANALYZE TABLE tb_vehicle_gps_segment_mileage; -- 3. 定期优化表以回收空间 -- OPTIMIZE TABLE tb_vehicle_gps_segment_mileage; -- 4. 查看分区使用情况 SELECT PARTITION_NAME as '分区名', PARTITION_METHOD as '分区方式', PARTITION_EXPRESSION as '分区表达式', TABLE_ROWS as '记录数', ROUND(DATA_LENGTH/1024/1024, 2) as '数据大小(MB)', ROUND(INDEX_LENGTH/1024/1024, 2) as '索引大小(MB)', PARTITION_COMMENT as '备注' FROM information_schema.PARTITIONS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'tb_vehicle_gps_segment_mileage' ORDER BY PARTITION_ORDINAL_POSITION; -- ======================================== -- 分区自动维护脚本(建议配置定时任务) -- ======================================== DELIMITER $$ CREATE PROCEDURE add_gps_segment_partition() BEGIN DECLARE next_month_date DATE; DECLARE partition_name VARCHAR(20); DECLARE next_partition_date DATE; -- 计算下个月的日期 SET next_month_date = DATE_ADD(CURDATE(), INTERVAL 2 MONTH); SET next_month_date = DATE_FORMAT(next_month_date, '%Y-%m-01'); -- 生成分区名称(例如:p202701) SET partition_name = CONCAT('p', DATE_FORMAT(next_month_date, '%Y%m')); -- 计算下一个分区的边界日期 SET next_partition_date = DATE_ADD(next_month_date, INTERVAL 1 MONTH); -- 动态添加分区 SET @sql = CONCAT( 'ALTER TABLE tb_vehicle_gps_segment_mileage ', 'REORGANIZE PARTITION pfuture INTO (', 'PARTITION ', partition_name, ' VALUES LESS THAN (TO_DAYS(''', next_partition_date, ''')),', 'PARTITION pfuture VALUES LESS THAN MAXVALUE', ')' ); PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; SELECT CONCAT('成功添加分区: ', partition_name, ', 边界日期: ', next_partition_date) as result; END$$ DELIMITER ; -- 使用方法:每月执行一次 -- CALL add_gps_segment_partition(); -- ======================================== -- 历史数据归档策略(可选) -- ======================================== -- 方案1:导出历史分区到归档表 -- CREATE TABLE tb_vehicle_gps_segment_mileage_archive LIKE tb_vehicle_gps_segment_mileage; -- ALTER TABLE tb_vehicle_gps_segment_mileage_archive REMOVE PARTITIONING; -- INSERT INTO tb_vehicle_gps_segment_mileage_archive -- SELECT * FROM tb_vehicle_gps_segment_mileage PARTITION(p202401); -- 方案2:导出到文件 -- SELECT * INTO OUTFILE '/tmp/gps_segment_202401.csv' -- FIELDS TERMINATED BY ',' OPTIONALLY ENCLOSED BY '"' -- LINES TERMINATED BY '\n' -- FROM tb_vehicle_gps_segment_mileage PARTITION(p202401); -- 方案3:定期删除超过N个月的历史数据 -- 例如:删除12个月之前的数据 -- ALTER TABLE tb_vehicle_gps_segment_mileage -- DROP PARTITION p202401; -- ======================================== -- 监控和告警(建议) -- ======================================== -- 监控各分区的数据量增长 CREATE VIEW v_gps_segment_partition_stats AS SELECT PARTITION_NAME as partition_name, TABLE_ROWS as row_count, ROUND(DATA_LENGTH/1024/1024, 2) as data_size_mb, ROUND(INDEX_LENGTH/1024/1024, 2) as index_size_mb, ROUND((DATA_LENGTH + INDEX_LENGTH)/1024/1024, 2) as total_size_mb, CREATE_TIME as create_time, UPDATE_TIME as update_time FROM information_schema.PARTITIONS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'tb_vehicle_gps_segment_mileage' ORDER BY PARTITION_ORDINAL_POSITION; -- 查看分区统计 -- SELECT * FROM v_gps_segment_partition_stats;