-- ========================================
|
-- 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;
|