Files
ccdi/docs/plans/2026-02-28-database-migration.md

1249 lines
29 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 数据库迁移实施计划
> **For Claude:** REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task.
**Goal:** 创建自动化脚本完成 CCDI 数据库的完整导出和导入,包括表结构和数据,确保字符集正确无乱码
**Architecture:** 使用 mysqldump 命令导出数据库,分离表结构和数据为两个 SQL 文件,通过 Bash 脚本自动化管理导出和导入流程,配置文件管理多环境数据库连接信息
**Tech Stack:** Bash 脚本、mysqldump/mysql 命令行工具、UTF-8/utf8mb4 字符集
---
## Task 1: 创建配置文件模板和安全措施
**Files:**
- Create: `db_config.conf.template`
- Modify: `.gitignore`
**Step 1: 创建配置文件模板**
创建 `db_config.conf.template` 文件:
```bash
# 数据库迁移配置文件模板
# 使用方法:复制此文件为 db_config.conf 并填写实际值
# 源数据库配置(开发环境)
SOURCE_DB_HOST=116.62.17.81
SOURCE_DB_PORT=3306
SOURCE_DB_USER=root
SOURCE_DB_PASS=Kfcx@1234
SOURCE_DB_NAME=ccdi
# 生产环境数据库配置
PROD_DB_HOST=your_production_host
PROD_DB_PORT=3306
PROD_DB_USER=your_production_user
PROD_DB_PASS=your_production_password
PROD_DB_NAME=ccdi
# 测试环境数据库配置(可选)
TEST_DB_HOST=your_test_host
TEST_DB_PORT=3306
TEST_DB_USER=your_test_user
TEST_DB_PASS=your_test_password
TEST_DB_NAME=ccdi
# 导出文件配置
BACKUP_DIR=doc/database/backup
STRUCTURE_FILE=ccdi_structure.sql
DATA_FILE=ccdi_data.sql
# mysqldump 参数配置
CHARACTER_SET=utf8mb4
MAX_ALLOWED_PACKET=512M
```
**Step 2: 更新 .gitignore 文件**
`.gitignore` 文件末尾添加:
```
# 数据库配置文件(包含敏感信息)
db_config.conf
```
**Step 3: 提交配置模板**
```bash
git add db_config.conf.template .gitignore
git commit -m "feat: 添加数据库迁移配置模板和安全措施"
```
---
## Task 2: 创建备份目录结构
**Files:**
- Create: `doc/database/backup/.gitkeep`
**Step 1: 创建备份目录**
```bash
mkdir -p doc/database/backup
```
**Step 2: 创建 .gitkeep 文件保持目录**
```bash
touch doc/database/backup/.gitkeep
```
**Step 3: 提交目录结构**
```bash
git add doc/database/backup/.gitkeep
git commit -m "feat: 创建数据库备份目录结构"
```
---
## Task 3: 创建自动化导出脚本(框架和导出功能)
**Files:**
- Create: `export_database.sh`
**Step 1: 创建脚本文件并添加基本框架**
创建 `export_database.sh` 文件:
```bash
#!/bin/bash
# CCDI 数据库迁移自动化脚本
# 功能:数据库导出和导入自动化
set -e # 遇到错误立即退出
# 颜色定义
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m' # No Color
# 日志函数
log_info() {
echo -e "${GREEN}[INFO]${NC} $1"
}
log_warn() {
echo -e "${YELLOW}[WARN]${NC} $1"
}
log_error() {
echo -e "${RED}[ERROR]${NC} $1"
}
# 脚本目录
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
CONFIG_FILE="${SCRIPT_DIR}/db_config.conf"
# 检查配置文件
check_config() {
if [ ! -f "$CONFIG_FILE" ]; then
log_error "配置文件不存在: $CONFIG_FILE"
log_info "请先复制配置模板: cp db_config.conf.template db_config.conf"
log_info "然后编辑 db_config.conf 填写实际数据库连接信息"
exit 1
fi
# 加载配置文件
source "$CONFIG_FILE"
log_info "配置文件加载成功"
}
# 检查 mysqldump 命令
check_mysqldump() {
if ! command -v mysqldump &> /dev/null; then
log_error "mysqldump 命令未找到"
log_info "请安装 MySQL 客户端工具"
exit 1
fi
log_info "mysqldump 命令检查通过"
}
# 创建备份目录
create_backup_dir() {
if [ ! -d "$BACKUP_DIR" ]; then
mkdir -p "$BACKUP_DIR"
log_info "创建备份目录: $BACKUP_DIR"
fi
}
# 导出表结构
export_structure() {
log_info "开始导出表结构..."
local output_file="${BACKUP_DIR}/${STRUCTURE_FILE}"
# 创建临时文件
local temp_file=$(mktemp)
# 导出表结构到临时文件
mysqldump -h "$SOURCE_DB_HOST" \
-P "$SOURCE_DB_PORT" \
-u "$SOURCE_DB_USER" \
-p"$SOURCE_DB_PASS" \
--no-data \
--skip-triggers \
--skip-add-drop-table \
--default-character-set=$CHARACTER_SET \
--single-transaction \
--max_allowed_packet=$MAX_ALLOWED_PACKET \
"$SOURCE_DB_NAME" > "$temp_file" 2>/dev/null
if [ $? -eq 0 ]; then
# 添加字符集声明到文件头部
{
echo "-- CCDI 数据库表结构"
echo "-- 导出时间: $(date '+%Y-%m-%d %H:%M:%S')"
echo "-- 源数据库: ${SOURCE_DB_HOST}:${SOURCE_DB_PORT}/${SOURCE_DB_NAME}"
echo "-- 字符集: ${CHARACTER_SET}"
echo ""
echo "SET NAMES utf8mb4;"
echo "SET CHARACTER SET utf8mb4;"
echo "SET GLOBAL character_set_client=utf8mb4;"
echo "SET GLOBAL character_set_connection=utf8mb4;"
echo "SET GLOBAL character_set_results=utf8mb4;"
echo ""
cat "$temp_file"
} > "$output_file"
rm -f "$temp_file"
log_info "表结构导出成功: $output_file"
log_info "文件大小: $(du -h "$output_file" | cut -f1)"
else
rm -f "$temp_file"
log_error "表结构导出失败"
exit 1
fi
}
# 导出数据
export_data() {
log_info "开始导出数据..."
local output_file="${BACKUP_DIR}/${DATA_FILE}"
# 创建临时文件
local temp_file=$(mktemp)
# 导出数据到临时文件
mysqldump -h "$SOURCE_DB_HOST" \
-P "$SOURCE_DB_PORT" \
-u "$SOURCE_DB_USER" \
-p"$SOURCE_DB_PASS" \
--no-create-info \
--skip-triggers \
--default-character-set=$CHARACTER_SET \
--single-transaction \
--complete-insert \
--extended-insert \
--max_allowed_packet=$MAX_ALLOWED_PACKET \
"$SOURCE_DB_NAME" > "$temp_file" 2>/dev/null
if [ $? -eq 0 ]; then
# 添加字符集声明到文件头部
{
echo "-- CCDI 数据库数据"
echo "-- 导出时间: $(date '+%Y-%m-%d %H:%M:%S')"
echo "-- 源数据库: ${SOURCE_DB_HOST}:${SOURCE_DB_PORT}/${SOURCE_DB_NAME}"
echo "-- 字符集: ${CHARACTER_SET}"
echo ""
echo "SET NAMES utf8mb4;"
echo "SET CHARACTER SET utf8mb4;"
echo "SET GLOBAL character_set_client=utf8mb4;"
echo "SET GLOBAL character_set_connection=utf8mb4;"
echo "SET GLOBAL character_set_results=utf8mb4;"
echo "SET FOREIGN_KEY_CHECKS=0;"
echo ""
cat "$temp_file"
echo ""
echo "SET FOREIGN_KEY_CHECKS=1;"
} > "$output_file"
rm -f "$temp_file"
log_info "数据导出成功: $output_file"
log_info "文件大小: $(du -h "$output_file" | cut -f1)"
else
rm -f "$temp_file"
log_error "数据导出失败"
exit 1
fi
}
# 验证导出文件
verify_export() {
log_info "验证导出文件..."
local structure_file="${BACKUP_DIR}/${STRUCTURE_FILE}"
local data_file="${BACKUP_DIR}/${DATA_FILE}"
# 检查文件是否存在
if [ ! -f "$structure_file" ]; then
log_error "表结构文件不存在: $structure_file"
exit 1
fi
if [ ! -f "$data_file" ]; then
log_error "数据文件不存在: $data_file"
exit 1
fi
# 检查字符集声明
if ! grep -q "SET NAMES utf8mb4" "$structure_file"; then
log_error "表结构文件缺少字符集声明"
exit 1
fi
if ! grep -q "SET NAMES utf8mb4" "$data_file"; then
log_error "数据文件缺少字符集声明"
exit 1
fi
log_info "导出文件验证通过"
log_info "表结构文件: $structure_file ($(du -h "$structure_file" | cut -f1))"
log_info "数据文件: $data_file ($(du -h "$data_file" | cut -f1))"
}
# 导出数据库
export_database() {
log_info "========== 开始导出数据库 =========="
check_config
check_mysqldump
create_backup_dir
export_structure
export_data
verify_export
log_info "========== 数据库导出完成 =========="
}
# 使用帮助
show_usage() {
echo "用法: $0 <command>"
echo ""
echo "命令:"
echo " export 导出数据库"
echo " help 显示帮助信息"
echo ""
echo "示例:"
echo " $0 export # 导出数据库到 doc/database/backup/ 目录"
}
# 主函数
main() {
case "$1" in
export)
export_database
;;
help|--help|-h)
show_usage
;;
*)
log_error "未知命令: $1"
show_usage
exit 1
;;
esac
}
# 执行主函数
main "$@"
```
**Step 2: 设置脚本执行权限**
```bash
chmod +x export_database.sh
```
**Step 3: 提交导出脚本**
```bash
git add export_database.sh
git commit -m "feat: 创建数据库导出自动化脚本"
```
---
## Task 4: 执行数据库导出
**Files:**
- Generate: `doc/database/backup/ccdi_structure.sql`
- Generate: `doc/database/backup/ccdi_data.sql`
**Step 1: 创建配置文件**
```bash
cp db_config.conf.template db_config.conf
```
**Step 2: 验证配置文件内容**
检查 `db_config.conf` 文件确保源数据库配置正确:
```bash
cat db_config.conf | grep "SOURCE_DB"
```
预期输出:
```
SOURCE_DB_HOST=116.62.17.81
SOURCE_DB_PORT=3306
SOURCE_DB_USER=root
SOURCE_DB_PASS=Kfcx@1234
SOURCE_DB_NAME=ccdi
```
**Step 3: 执行导出脚本**
```bash
./export_database.sh export
```
预期输出:
```
[INFO] 配置文件加载成功
[INFO] mysqldump 命令检查通过
[INFO] 开始导出表结构...
[INFO] 表结构导出成功: doc/database/backup/ccdi_structure.sql
[INFO] 文件大小: XXX
[INFO] 开始导出数据...
[INFO] 数据导出成功: doc/database/backup/ccdi_data.sql
[INFO] 文件大小: XXX
[INFO] 验证导出文件...
[INFO] 导出文件验证通过
[INFO] ========== 数据库导出完成 ==========
```
**Step 4: 验证导出文件**
检查表结构文件头部:
```bash
head -20 doc/database/backup/ccdi_structure.sql
```
预期输出应包含:
```sql
-- CCDI 数据库表结构
-- 导出时间: 2026-02-28 XX:XX:XX
-- 源数据库: 116.62.17.81:3306/ccdi
-- 字符集: utf8mb4
SET NAMES utf8mb4;
SET CHARACTER SET utf8mb4;
...
```
检查数据文件头部:
```bash
head -20 doc/database/backup/ccdi_data.sql
```
预期输出应包含:
```sql
-- CCDI 数据库数据
-- 导出时间: 2026-02-28 XX:XX:XX
-- 源数据库: 116.62.17.81:3306/ccdi
-- 字符集: utf8mb4
SET NAMES utf8mb4;
SET CHARACTER SET utf8mb4;
SET FOREIGN_KEY_CHECKS=0;
...
```
**Step 5: 检查文件大小**
```bash
ls -lh doc/database/backup/
```
预期输出应显示两个文件的大小。
---
## Task 5: 添加导入功能到脚本
**Files:**
- Modify: `export_database.sh` (添加导入函数)
**Step 1: 在脚本中添加导入函数**
`export_database.sh` 文件的 `verify_export()` 函数后添加以下函数:
```bash
# 导入表结构
import_structure() {
local env_type=$1
local db_host db_port db_user db_pass db_name
case "$env_type" in
production|prod)
db_host="$PROD_DB_HOST"
db_port="$PROD_DB_PORT"
db_user="$PROD_DB_USER"
db_pass="$PROD_DB_PASS"
db_name="$PROD_DB_NAME"
;;
test)
db_host="$TEST_DB_HOST"
db_port="$TEST_DB_PORT"
db_user="$TEST_DB_USER"
db_pass="$TEST_DB_PASS"
db_name="$TEST_DB_NAME"
;;
*)
log_error "未知的环境类型: $env_type"
exit 1
;;
esac
log_info "导入表结构到 ${env_type} 环境: ${db_host}:${db_port}/${db_name}"
local structure_file="${BACKUP_DIR}/${STRUCTURE_FILE}"
if [ ! -f "$structure_file" ]; then
log_error "表结构文件不存在: $structure_file"
log_info "请先执行导出: ./export_database.sh export"
exit 1
fi
# 导入表结构
mysql -h "$db_host" \
-P "$db_port" \
-u "$db_user" \
-p"$db_pass" \
--default-character-set=$CHARACTER_SET \
"$db_name" < "$structure_file" 2>/dev/null
if [ $? -eq 0 ]; then
log_info "表结构导入成功"
else
log_error "表结构导入失败"
exit 1
fi
}
# 导入数据
import_data() {
local env_type=$1
local db_host db_port db_user db_pass db_name
case "$env_type" in
production|prod)
db_host="$PROD_DB_HOST"
db_port="$PROD_DB_PORT"
db_user="$PROD_DB_USER"
db_pass="$PROD_DB_PASS"
db_name="$PROD_DB_NAME"
;;
test)
db_host="$TEST_DB_HOST"
db_port="$TEST_DB_PORT"
db_user="$TEST_DB_USER"
db_pass="$TEST_DB_PASS"
db_name="$TEST_DB_NAME"
;;
*)
log_error "未知的环境类型: $env_type"
exit 1
;;
esac
log_info "导入数据到 ${env_type} 环境: ${db_host}:${db_port}/${db_name}"
local data_file="${BACKUP_DIR}/${DATA_FILE}"
if [ ! -f "$data_file" ]; then
log_error "数据文件不存在: $data_file"
log_info "请先执行导出: ./export_database.sh export"
exit 1
fi
# 导入数据
mysql -h "$db_host" \
-P "$db_port" \
-u "$db_user" \
-p"$db_pass" \
--default-character-set=$CHARACTER_SET \
"$db_name" < "$data_file" 2>/dev/null
if [ $? -eq 0 ]; then
log_info "数据导入成功"
else
log_error "数据导入失败"
exit 1
fi
}
# 验证导入结果
verify_import() {
local env_type=$1
local db_host db_port db_user db_pass db_name
case "$env_type" in
production|prod)
db_host="$PROD_DB_HOST"
db_port="$PROD_DB_PORT"
db_user="$PROD_DB_USER"
db_pass="$PROD_DB_PASS"
db_name="$PROD_DB_NAME"
;;
test)
db_host="$TEST_DB_HOST"
db_port="$TEST_DB_PORT"
db_user="$TEST_DB_USER"
db_pass="$TEST_DB_PASS"
db_name="$TEST_DB_NAME"
;;
*)
log_error "未知的环境类型: $env_type"
exit 1
;;
esac
log_info "验证导入结果..."
# 查询表数量
local table_count=$(mysql -h "$db_host" \
-P "$db_port" \
-u "$db_user" \
-p"$db_pass" \
--default-character-set=$CHARACTER_SET \
-N -e "SELECT COUNT(*) FROM information_schema.tables WHERE table_schema='$db_name';" "$db_name" 2>/dev/null)
log_info "目标数据库表数量: $table_count"
# 查询关键表行数示例sys_user 表)
local user_count=$(mysql -h "$db_host" \
-P "$db_port" \
-u "$db_user" \
-p"$db_pass" \
--default-character-set=$CHARACTER_SET \
-N -e "SELECT COUNT(*) FROM sys_user;" "$db_name" 2>/dev/null)
log_info "sys_user 表数据行数: $user_count"
# 检查数据库字符集
local db_charset=$(mysql -h "$db_host" \
-P "$db_port" \
-u "$db_user" \
-p"$db_pass" \
--default-character-set=$CHARACTER_SET \
-N -e "SELECT DEFAULT_CHARACTER_SET_NAME FROM information_schema.schemata WHERE schema_name='$db_name';" 2>/dev/null)
log_info "数据库字符集: $db_charset"
}
# 导入数据库
import_database() {
local env_type=$1
if [ -z "$env_type" ]; then
log_error "请指定目标环境: production 或 test"
log_info "用法: $0 import [production|test]"
exit 1
fi
log_info "========== 开始导入数据库到 ${env_type} 环境 =========="
check_config
import_structure "$env_type"
import_data "$env_type"
verify_import "$env_type"
log_info "========== 数据库导入完成 =========="
}
```
**Step 2: 更新 show_usage() 函数**
替换 `show_usage()` 函数为:
```bash
# 使用帮助
show_usage() {
echo "用法: $0 <command> [options]"
echo ""
echo "命令:"
echo " export 导出数据库"
echo " import <env> 导入数据库到指定环境"
echo " help 显示帮助信息"
echo ""
echo "环境:"
echo " production, prod 生产环境"
echo " test 测试环境"
echo ""
echo "示例:"
echo " $0 export # 导出数据库到 doc/database/backup/ 目录"
echo " $0 import test # 导入数据库到测试环境"
echo " $0 import prod # 导入数据库到生产环境"
}
```
**Step 3: 更新 main() 函数**
替换 `main()` 函数为:
```bash
# 主函数
main() {
case "$1" in
export)
export_database
;;
import)
import_database "$2"
;;
help|--help|-h)
show_usage
;;
*)
log_error "未知命令: $1"
show_usage
exit 1
;;
esac
}
```
**Step 4: 提交导入功能**
```bash
git add export_database.sh
git commit -m "feat: 添加数据库导入功能到自动化脚本"
```
---
## Task 6: 创建操作指南文档
**Files:**
- Create: `doc/database/backup/export_guide.md`
**Step 1: 创建操作指南文档**
创建 `doc/database/backup/export_guide.md` 文件:
```markdown
# CCDI 数据库迁移操作指南
## 概述
本文档提供 CCDI 纪检初核系统数据库迁移的详细操作步骤,包括从开发环境导出数据库和导入到生产/测试环境。
## 前置条件
### 必需工具
- MySQL 客户端工具(包含 mysqldump 和 mysql 命令)
- Bash shell 环境
- 网络访问权限(能连接源数据库和目标数据库)
### 检查 mysqldump 是否安装
```bash
mysqldump --version
```
如果未安装,请根据操作系统安装 MySQL 客户端:
- **Windows**: 安装 MySQL Community Server
- **Linux (Ubuntu/Debian)**: `sudo apt-get install mysql-client`
- **Linux (CentOS/RHEL)**: `sudo yum install mysql`
- **macOS**: `brew install mysql-client`
## 配置步骤
### 1. 创建配置文件
```bash
# 复制配置模板
cp db_config.conf.template db_config.conf
# 编辑配置文件
nano db_config.conf # 或使用其他编辑器
```
### 2. 填写配置信息
编辑 `db_config.conf` 文件,填写以下信息:
**源数据库(开发环境):**
- `SOURCE_DB_HOST`: 开发环境数据库地址
- `SOURCE_DB_PORT`: 数据库端口(默认 3306
- `SOURCE_DB_USER`: 数据库用户名
- `SOURCE_DB_PASS`: 数据库密码
- `SOURCE_DB_NAME`: 数据库名称ccdi
**生产环境数据库:**
- `PROD_DB_HOST`: 生产环境数据库地址
- `PROD_DB_PORT`: 数据库端口
- `PROD_DB_USER`: 生产环境数据库用户名
- `PROD_DB_PASS`: 生产环境数据库密码
- `PROD_DB_NAME`: 数据库名称ccdi
**测试环境数据库(可选):**
- `TEST_DB_HOST`: 测试环境数据库地址
- `TEST_DB_PORT`: 数据库端口
- `TEST_DB_USER`: 测试环境数据库用户名
- `TEST_DB_PASS`: 测试环境数据库密码
- `TEST_DB_NAME`: 数据库名称ccdi
### 3. 验证配置
检查配置文件格式:
```bash
cat db_config.conf | grep -E "^[A-Z]"
```
确保所有必需的配置项都已填写。
## 数据库导出
### 执行导出
```bash
./export_database.sh export
```
### 预期输出
```
[INFO] 配置文件加载成功
[INFO] mysqldump 命令检查通过
[INFO] 开始导出表结构...
[INFO] 表结构导出成功: doc/database/backup/ccdi_structure.sql
[INFO] 文件大小: XXX
[INFO] 开始导出数据...
[INFO] 数据导出成功: doc/database/backup/ccdi_data.sql
[INFO] 文件大小: XXX
[INFO] 验证导出文件...
[INFO] 导出文件验证通过
[INFO] ========== 数据库导出完成 ==========
```
### 验证导出文件
**1. 检查文件是否存在**
```bash
ls -lh doc/database/backup/
```
应该看到:
- `ccdi_structure.sql` - 表结构文件
- `ccdi_data.sql` - 数据文件
**2. 检查字符集声明**
```bash
head -20 doc/database/backup/ccdi_structure.sql
```
应该包含:
```sql
SET NAMES utf8mb4;
SET CHARACTER SET utf8mb4;
```
**3. 检查文件内容**
```bash
# 查看表结构
grep "CREATE TABLE" doc/database/backup/ccdi_structure.sql | wc -l
# 查看数据量INSERT 语句数量)
grep "INSERT" doc/database/backup/ccdi_data.sql | wc -l
```
## 数据库导入
### 准备工作
**1. 确认目标数据库已创建**
连接到目标数据库服务器:
```bash
mysql -h 目标IP -P 3306 -u 用户名 -p
```
创建数据库(如果不存在):
```sql
CREATE DATABASE ccdi CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;
```
**2. 确认用户权限**
目标数据库用户需要以下权限:
- CREATE、ALTER、DROP创建和修改表
- INSERT、UPDATE、DELETE数据操作
- INDEX创建索引
- REFERENCES外键约束
### 导入到测试环境
```bash
./export_database.sh import test
```
### 导入到生产环境
```bash
./export_database.sh import production
```
或简写:
```bash
./export_database.sh import prod
```
### 预期输出
```
[INFO] ========== 开始导入数据库到 test 环境 ==========
[INFO] 导入表结构到 test 环境: XXX:3306/ccdi
[INFO] 表结构导入成功
[INFO] 导入数据到 test 环境: XXX:3306/ccdi
[INFO] 数据导入成功
[INFO] 验证导入结果...
[INFO] 目标数据库表数量: XX
[INFO] sys_user 表数据行数: XX
[INFO] 数据库字符集: utf8mb4
[INFO] ========== 数据库导入完成 ==========
```
## 导入后验证
### 1. 验证表数量
连接到目标数据库:
```bash
mysql -h 目标IP -P 3306 -u 用户名 -p ccdi
```
查询表数量:
```sql
SELECT COUNT(*) FROM information_schema.tables
WHERE table_schema='ccdi';
```
对比源数据库和目标数据库的表数量是否一致。
### 2. 验证数据行数
查询各表数据行数:
```sql
SELECT table_name, table_rows
FROM information_schema.tables
WHERE table_schema='ccdi'
ORDER BY table_rows DESC
LIMIT 20;
```
对比源数据库和目标数据库的关键表行数。
### 3. 验证字符集
检查数据库字符集:
```sql
SHOW CREATE DATABASE ccdi;
```
应该显示:`DEFAULT CHARACTER SET utf8mb4`
检查表字符集:
```sql
SHOW CREATE TABLE sys_user;
```
应该显示:`ENGINE=InnoDB DEFAULT CHARSET=utf8mb4`
### 4. 验证中文数据
查询包含中文的数据:
```sql
-- 查询用户表
SELECT user_name, nick_name FROM sys_user LIMIT 10;
-- 查询字典数据
SELECT dict_label, dict_value FROM sys_dict_data LIMIT 10;
```
确保中文字符显示正常,无乱码。
### 5. 应用程序连接测试
修改应用程序配置文件连接到目标数据库,启动应用程序进行功能测试。
## 完整迁移流程示例
### 场景:从开发环境迁移到生产环境
**1. 配置数据库连接**
```bash
cp db_config.conf.template db_config.conf
nano db_config.conf
# 填写开发环境和生产环境数据库信息
```
**2. 导出数据库**
```bash
./export_database.sh export
```
**3. 验证导出文件**
```bash
ls -lh doc/database/backup/
head -20 doc/database/backup/ccdi_structure.sql
```
**4. 先在测试环境验证**
```bash
./export_database.sh import test
```
**5. 验证测试环境**
- 连接测试数据库验证数据
- 应用程序连接测试环境进行功能测试
**6. 导入到生产环境**
```bash
./export_database.sh import production
```
**7. 验证生产环境**
- 连接生产数据库验证数据
- 应用程序连接生产环境进行功能测试
**8. 完成迁移**
## 常见问题
### 1. mysqldump: command not found
**原因**: MySQL 客户端未安装或未添加到 PATH
**解决**:
- 安装 MySQL 客户端工具
- 或使用完整路径:`/usr/bin/mysqldump`
### 2. 配置文件不存在
**错误信息**: `配置文件不存在: db_config.conf`
**解决**:
```bash
cp db_config.conf.template db_config.conf
# 编辑 db_config.conf 填写实际配置
```
### 3. 连接数据库失败
**可能原因**:
- 数据库地址、端口、用户名或密码错误
- 防火墙阻止连接
- 数据库服务未启动
**解决**:
- 检查配置文件中的连接信息
- 使用 mysql 命令手动测试连接
- 检查防火墙规则
### 4. 导入时字符集乱码
**原因**: 未正确指定字符集
**解决**:
- 确保导出文件包含字符集声明
- 导入命令添加 `--default-character-set=utf8mb4` 参数
- 脚本已自动处理,如仍有问题请检查数据库默认字符集
### 5. 外键约束失败
**错误信息**: `ERROR 1452 (23000): Cannot add or update a child row: a foreign key constraint fails`
**解决**:
- 脚本已自动添加 `SET FOREIGN_KEY_CHECKS=0;``SET FOREIGN_KEY_CHECKS=1;`
- 如仍有问题,请检查数据完整性
### 6. 数据包过大
**错误信息**: `ERROR 1153 (08S01): Got a packet bigger than 'max_allowed_packet' bytes`
**解决**:
- 配置文件中的 `MAX_ALLOWED_PACKET=512M` 已处理此问题
- 如数据量特别大,可增大此值
### 7. 权限不足
**错误信息**: `ERROR 1044 (42000): Access denied for user`
**解决**:
- 使用具有足够权限的用户(如 root
- 或授予用户必要权限
## 回滚方案
如果迁移失败或出现问题:
1. **保留源数据库**: 不要删除开发环境数据库
2. **重新迁移**: 修复问题后重新执行迁移流程
3. **从备份恢复**: 如生产环境有备份,可从备份恢复
## 注意事项
1. **安全性**:
- `db_config.conf` 包含敏感信息,已添加到 `.gitignore`
- 不要将配置文件提交到版本控制系统
- 迁移完成后建议删除配置文件中的密码
2. **性能**:
- 大数据库导出/导入可能需要较长时间
- 建议在低峰期执行迁移
- 确保有足够的磁盘空间
3. **数据一致性**:
- 导出期间源数据库应避免写入操作
- 或使用 `--single-transaction` 参数(已包含)
4. **字符集**:
- 确保所有步骤都使用 utf8mb4 字符集
- 验证阶段重点检查中文数据
## 技术支持
如遇到问题:
1. 检查本文档的常见问题部分
2. 查看脚本执行的错误信息
3. 检查数据库连接和权限
4. 查看数据库日志
## 相关文件
- 自动化脚本: `export_database.sh`
- 配置模板: `db_config.conf.template`
- 实际配置: `db_config.conf`
- 表结构文件: `doc/database/backup/ccdi_structure.sql`
- 数据文件: `doc/database/backup/ccdi_data.sql`
- 设计文档: `docs/plans/2026-02-28-database-migration-design.md`
```
**Step 2: 提交操作指南**
```bash
git add doc/database/backup/export_guide.md
git commit -m "docs: 添加数据库迁移操作指南"
```
---
## Task 7: 完整验证流程
**Files:**
- 无文件修改,仅验证操作
**Step 1: 验证脚本帮助信息**
```bash
./export_database.sh help
```
预期输出:
```
用法: ./export_database.sh <command> [options]
命令:
export 导出数据库
import <env> 导入数据库到指定环境
help 显示帮助信息
环境:
production, prod 生产环境
test 测试环境
示例:
./export_database.sh export # 导出数据库到 doc/database/backup/ 目录
./export_database.sh import test # 导入数据库到测试环境
./export_database.sh import prod # 导入数据库到生产环境
```
**Step 2: 验证配置文件**
```bash
cat db_config.conf
```
确保包含所有必需配置项。
**Step 3: 验证导出文件**
```bash
# 检查文件存在
ls -lh doc/database/backup/ccdi_*.sql
# 检查文件内容
head -30 doc/database/backup/ccdi_structure.sql
head -30 doc/database/backup/ccdi_data.sql
# 统计表数量
grep "CREATE TABLE" doc/database/backup/ccdi_structure.sql | wc -l
# 统计 INSERT 语句
grep "INSERT INTO" doc/database/backup/ccdi_data.sql | wc -l
```
**Step 4: 创建验证报告**
创建临时验证报告:
```bash
cat > /tmp/migration_verify.txt << 'EOF'
数据库迁移验证报告
==================
导出时间: $(date)
文件信息:
- 表结构文件: $(ls -lh doc/database/backup/ccdi_structure.sql)
- 数据文件: $(ls -lh doc/database/backup/ccdi_data.sql)
表数量: $(grep "CREATE TABLE" doc/database/backup/ccdi_structure.sql | wc -l)
字符集检查:
$(grep "SET NAMES utf8mb4" doc/database/backup/ccdi_structure.sql)
验证结果: 通过
EOF
cat /tmp/migration_verify.txt
```
---
## 成功标准
完成后,应满足以下所有条件:
1. ✅ 配置文件模板 `db_config.conf.template` 已创建
2.`db_config.conf` 已添加到 `.gitignore`
3. ✅ 自动化脚本 `export_database.sh` 可执行
4. ✅ 执行 `./export_database.sh export` 成功生成两个 SQL 文件
5.`ccdi_structure.sql` 包含所有表结构和字符集声明
6.`ccdi_data.sql` 包含所有数据和字符集声明
7. ✅ SQL 文件无乱码,字符集正确
8. ✅ 操作指南文档清晰完整
9. ✅ 所有代码已提交到 Git
## 执行建议
**建议执行顺序**
1. 在测试环境完整执行一遍流程
2. 验证测试环境导入的数据完整性
3. 确认无问题后,在生产环境执行
4. 验证生产环境数据完整性
5. 应用程序连接测试
**时间估算**
- Task 1-3: 15分钟脚本开发
- Task 4: 20-30分钟导出执行取决于数据量
- Task 5: 10分钟添加导入功能
- Task 6-7: 15分钟文档和验证
**总计**: 约 60-70 分钟