From 3b45a669f9869f7e92ad09d6175d88dfa598d8fd Mon Sep 17 00:00:00 2001 From: marsal Date: Tue, 7 Jul 2026 18:34:47 +0800 Subject: [PATCH] =?UTF-8?q?feat(db):=20V15=20=E4=B8=BA=E9=AB=98=E9=A2=91?= =?UTF-8?q?=E8=A1=A8=E6=B7=BB=E5=8A=A0=20RANGE=20=E6=9C=88=E5=88=86?= =?UTF-8?q?=E5=8C=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../migration/V15__add_range_partitions.sql | 144 ++++++++++++++++++ scripts/extend-mysql-partitions.sh | 36 +++++ 2 files changed, 180 insertions(+) create mode 100644 backend/mci-server/src/main/resources/db/migration/V15__add_range_partitions.sql create mode 100755 scripts/extend-mysql-partitions.sh diff --git a/backend/mci-server/src/main/resources/db/migration/V15__add_range_partitions.sql b/backend/mci-server/src/main/resources/db/migration/V15__add_range_partitions.sql new file mode 100644 index 0000000..abf76b8 --- /dev/null +++ b/backend/mci-server/src/main/resources/db/migration/V15__add_range_partitions.sql @@ -0,0 +1,144 @@ +-- Phase 1.5: 为高频表添加 MySQL RANGE 月分区(分区键 create_time) +-- 架构设计文档:docs/architecture/2026-07-06-message-customer-interaction-design.md 第 2.6.2 节 + +-- ============================ +-- 1. mci_message_event(消息事件表) +-- ============================ +-- 调整主键/唯一键,确保包含分区键 create_time +ALTER TABLE mci_message_event + DROP PRIMARY KEY, + ADD PRIMARY KEY (id, create_time), + DROP INDEX uk_event_id, + ADD UNIQUE KEY uk_event_id (event_id, create_time); + +-- 改为 RANGE COLUMNS 月分区,并预创建 2026-07 至 2026-12 分区 +ALTER TABLE mci_message_event + PARTITION BY RANGE COLUMNS(create_time) ( + PARTITION p202607 VALUES LESS THAN ('2026-08-01'), + PARTITION p202608 VALUES LESS THAN ('2026-09-01'), + PARTITION p202609 VALUES LESS THAN ('2026-10-01'), + PARTITION p202610 VALUES LESS THAN ('2026-11-01'), + PARTITION p202611 VALUES LESS THAN ('2026-12-01'), + PARTITION p202612 VALUES LESS THAN ('2027-01-01'), + PARTITION pfuture VALUES LESS THAN MAXVALUE + ); + +-- ============================ +-- 2. mci_send_record(发送记录表) +-- ============================ +ALTER TABLE mci_send_record + DROP PRIMARY KEY, + ADD PRIMARY KEY (id, create_time), + DROP INDEX uk_task_target, + ADD UNIQUE KEY uk_task_target (task_id, conversation_id, person_id, create_time); + +ALTER TABLE mci_send_record + PARTITION BY RANGE COLUMNS(create_time) ( + PARTITION p202607 VALUES LESS THAN ('2026-08-01'), + PARTITION p202608 VALUES LESS THAN ('2026-09-01'), + PARTITION p202609 VALUES LESS THAN ('2026-10-01'), + PARTITION p202610 VALUES LESS THAN ('2026-11-01'), + PARTITION p202611 VALUES LESS THAN ('2026-12-01'), + PARTITION p202612 VALUES LESS THAN ('2027-01-01'), + PARTITION pfuture VALUES LESS THAN MAXVALUE + ); + +-- ============================ +-- 3. mci_conversation(会话表,可选但推荐) +-- ============================ +-- MySQL 不支持外键与分区共存,因此需要先删除与会话表相关的外键约束。 +-- 这些外键属于数据库级完整性约束,应用层(MyBatis-Plus)不依赖它们。 +ALTER TABLE mci_conversation_participant + DROP FOREIGN KEY fk_participant_conversation_id; + +ALTER TABLE mci_channel_account_conversation + DROP FOREIGN KEY fk_binding_conversation_id; + +ALTER TABLE mci_conversation_tag_binding + DROP FOREIGN KEY fk_conv_tag_conversation_id; + +ALTER TABLE mci_conversation + DROP FOREIGN KEY fk_conversation_owner_account; + +ALTER TABLE mci_conversation + DROP FOREIGN KEY fk_conversation_subject_id; + +-- 调整主键/唯一键,确保包含分区键 create_time +ALTER TABLE mci_conversation + DROP PRIMARY KEY, + ADD PRIMARY KEY (id, create_time), + DROP INDEX uk_tenant_channel_conv, + ADD UNIQUE KEY uk_tenant_channel_conv (tenant_code, channel_type, channel_conversation_id, deleted, create_time); + +ALTER TABLE mci_conversation + PARTITION BY RANGE COLUMNS(create_time) ( + PARTITION p202607 VALUES LESS THAN ('2026-08-01'), + PARTITION p202608 VALUES LESS THAN ('2026-09-01'), + PARTITION p202609 VALUES LESS THAN ('2026-10-01'), + PARTITION p202610 VALUES LESS THAN ('2026-11-01'), + PARTITION p202611 VALUES LESS THAN ('2026-12-01'), + PARTITION p202612 VALUES LESS THAN ('2027-01-01'), + PARTITION pfuture VALUES LESS THAN MAXVALUE + ); + +-- ============================ +-- 4. 创建定期扩展未来分区的存储过程 +-- ============================ +DELIMITER $$ + +CREATE PROCEDURE ExtendMonthlyPartitions( + IN p_table_name VARCHAR(64), + IN p_months_ahead INT +) +BEGIN + DECLARE v_current DATE; + DECLARE v_target DATE; + DECLARE v_partition_name VARCHAR(16); + DECLARE v_less_than VARCHAR(12); + DECLARE v_max_partition VARCHAR(64); + DECLARE v_sql VARCHAR(2000); + + -- 从下个月开始补充分区 + SET v_current = DATE_FORMAT(CURDATE() + INTERVAL 1 MONTH, '%Y-%m-01'); + SET v_target = DATE_FORMAT(CURDATE() + INTERVAL p_months_ahead MONTH, '%Y-%m-01'); + + -- 找到当前 MAXVALUE 分区名(通常为 pfuture) + SELECT PARTITION_NAME INTO v_max_partition + FROM information_schema.PARTITIONS + WHERE TABLE_SCHEMA = DATABASE() + AND TABLE_NAME = p_table_name + AND PARTITION_DESCRIPTION = 'MAXVALUE'; + + IF v_max_partition IS NULL THEN + SIGNAL SQLSTATE '45000' + SET MESSAGE_TEXT = 'No MAXVALUE partition found; table may not be range-partitioned'; + END IF; + + WHILE v_current <= v_target DO + SET v_partition_name = CONCAT('p', DATE_FORMAT(v_current, '%Y%m')); + SET v_less_than = DATE_FORMAT(v_current + INTERVAL 1 MONTH, '%Y-%m-%d'); + + -- 仅当分区不存在时才添加,保证幂等 + IF NOT EXISTS ( + SELECT 1 FROM information_schema.PARTITIONS + WHERE TABLE_SCHEMA = DATABASE() + AND TABLE_NAME = p_table_name + AND PARTITION_NAME = v_partition_name + ) THEN + SET v_sql = CONCAT( + 'ALTER TABLE ', p_table_name, + ' REORGANIZE PARTITION ', v_max_partition, + ' INTO (PARTITION ', v_partition_name, ' VALUES LESS THAN (''', v_less_than, '''), ', + 'PARTITION ', v_max_partition, ' VALUES LESS THAN MAXVALUE)' + ); + SET @sql = v_sql; + PREPARE stmt FROM @sql; + EXECUTE stmt; + DEALLOCATE PREPARE stmt; + END IF; + + SET v_current = v_current + INTERVAL 1 MONTH; + END WHILE; +END$$ + +DELIMITER ; diff --git a/scripts/extend-mysql-partitions.sh b/scripts/extend-mysql-partitions.sh new file mode 100755 index 0000000..e711f9c --- /dev/null +++ b/scripts/extend-mysql-partitions.sh @@ -0,0 +1,36 @@ +#!/usr/bin/env bash +# +# 扩展 MySQL RANGE 月分区 +# 为 mci_message_event / mci_send_record / mci_conversation 自动添加未来月份分区。 +# 依赖 V15__add_range_partitions.sql 中创建的存储过程 ExtendMonthlyPartitions。 +# +# 用法示例: +# ./scripts/extend-mysql-partitions.sh +# MYSQL_HOST=127.0.0.1 MYSQL_PORT=3306 MYSQL_USER=root MYSQL_PASSWORD=root MYSQL_DB=sino_mci ./scripts/extend-mysql-partitions.sh +# + +set -euo pipefail + +MYSQL_HOST="${MYSQL_HOST:-mci-mysql}" +MYSQL_PORT="${MYSQL_PORT:-3306}" +MYSQL_USER="${MYSQL_USER:-root}" +MYSQL_PASSWORD="${MYSQL_PASSWORD:-root}" +MYSQL_DB="${MYSQL_DB:-sino_mci}" +MONTHS_AHEAD="${MONTHS_AHEAD:-3}" + +TABLES=( + mci_message_event + mci_send_record + mci_conversation +) + +echo "Extending monthly partitions for database: ${MYSQL_DB}" +echo "Host: ${MYSQL_HOST}:${MYSQL_PORT}, User: ${MYSQL_USER}, Months ahead: ${MONTHS_AHEAD}" + +for table in "${TABLES[@]}"; do + echo " -> ${table}" + mysql -h "${MYSQL_HOST}" -P "${MYSQL_PORT}" -u "${MYSQL_USER}" -p"${MYSQL_PASSWORD}" "${MYSQL_DB}" \ + -e "CALL ExtendMonthlyPartitions('${table}', ${MONTHS_AHEAD});" +done + +echo "Done."