MySQL 数据库同步指南
本文提供两个 Bash 脚本,用于在本地和远程服务器之间同步 MySQL 数据库,支持本机运行的 MySQL(native
)和 Docker 容器中的 MySQL(docker
):
mysql_remote_to_local_sync.sh
:将远程服务器的 MySQL 数据库同步到本地。mysql_local_to_remote_sync.sh
:将本地 MySQL 数据库同步到远程服务器。
脚本特点:
- 支持从
.env
文件加载配置,定义默认变量。 - 运行前显示配置信息,要求用户确认(输入
y
或yes
)。 - 支持
native
模式的自定义 MySQL 端口(默认 3306)。 - 日志以中文输出到控制台,敏感信息(如密码)使用占位符。
- 检查必要命令(
mysql
、mysqldump
、docker
、ssh
)和数据库连接。 - 自动备份数据库,清理旧备份文件(保留最近 5 个)。
环境假设
- 远程服务器:
- IP:
<IP>
- SSH 用户:
<USER>
- MySQL 类型:
REMOTE_MYSQL_TYPE
(native
或docker
) - 端口:
REMOTE_MYSQL_PORT
(native
模式,默认 3306) - 容器名(
docker
模式):<SOURCE_CONTAINER>
或<TARGET_CONTAINER>
- 数据库:
<SOURCE_DB>
或<TARGET_DB>
- 用户:
<SOURCE_USER>
或<TARGET_USER>
- 密码:
<SOURCE_PASS>
或<TARGET_PASS>
- IP:
- 本地主机:
- MySQL 类型:
LOCAL_MYSQL_TYPE
(native
或docker
) - 端口:
LOCAL_MYSQL_PORT
(native
模式,默认 3306) - 容器名(
docker
模式):<SOURCE_CONTAINER>
或<TARGET_CONTAINER>
- 数据库:
<SOURCE_DB>
或<TARGET_DB>
- 用户:
<SOURCE_USER>
或<TARGET_USER>
- 密码:
<SOURCE_PASS>
或<TARGET_PASS>
- MySQL 类型:
- 备份目录:
<BACKUP_DIR>
- 要求:
- 本地和远程主机安装
docker
和ssh
(若使用docker
),或mysql
和mysqldump
(若使用native
)。 - SSH 配置免密登录。
- 可选:
.env
文件覆盖默认配置。
- 本地和远程主机安装
脚本功能
1. 远程到本地同步 (mysql_remote_to_local_sync.sh
)
-
功能:
- 从远程 MySQL(
native
或docker
)备份数据库<SOURCE_DB>
。 - 将备份文件传输到本地,导入本地 MySQL(
native
或docker
)。 - 清理旧备份文件(保留最近 5 个)。
- 支持
.env
文件配置、默认变量、用户确认。 - 日志以中文记录,包括
LOCAL_MYSQL_TYPE
和REMOTE_MYSQL_TYPE
检查。
- 从远程 MySQL(
-
流程:
- 加载
.env
文件,应用默认变量。 - 显示配置信息,要求用户输入
y/yes
确认。 - 检查命令(
ssh
、docker
、mysql
、mysqldump
)。 - 验证远程和本地数据库连接。
- 备份远程数据库到本地
<BACKUP_DIR>
。 - 导入备份到本地目标数据库。
- 清理旧备份文件。
- 加载
2. 本地到远程同步 (mysql_local_to_remote_sync.sh
)
-
功能:
- 从本地 MySQL(
native
或docker
)备份数据库<SOURCE_DB>
。 - 将备份文件传输到远程服务器,导入远程 MySQL(
native
或docker
)。 - 清理本地旧备份文件(保留最近 5 个)。
- 支持
.env
文件配置、默认变量、用户确认。 - 日志以中文记录,包括
LOCAL_MYSQL_TYPE
和REMOTE_MYSQL_TYPE
检查。
- 从本地 MySQL(
-
流程:
- 加载
.env
文件,应用默认变量。 - 显示配置信息,要求用户输入
y/yes
确认。 - 检查命令(
ssh
、docker
、mysql
、mysqldump
)。 - 验证本地和远程数据库连接。
- 备份本地数据库到
<BACKUP_DIR>
。 - 传输备份文件并导入远程目标数据库。
- 清理旧备份文件。
- 加载
配置步骤
1. 创建 .env
文件
在脚本所在目录创建 .env
文件,定义配置变量。示例:
REMOTE_USER=user
REMOTE_HOST=192.168.1.100
REMOTE_MYSQL_TYPE=docker
REMOTE_MYSQL_PORT=3306
SOURCE_CONTAINER=remote_mysql
SOURCE_USER=db_user
SOURCE_PASS=db_pass
SOURCE_DB=mydb
LOCAL_MYSQL_TYPE=docker
LOCAL_MYSQL_PORT=3306
TARGET_CONTAINER=local_mysql
TARGET_USER=db_user
TARGET_PASS=db_pass
TARGET_DB=localdb
BACKUP_DIR=/backup/mysql
-
默认变量(若
.env
未定义):DEFAULT_REMOTE_USER=<USER> DEFAULT_REMOTE_HOST=<IP> DEFAULT_REMOTE_MYSQL_TYPE=docker DEFAULT_REMOTE_MYSQL_PORT=3306 DEFAULT_SOURCE_CONTAINER=<SOURCE_CONTAINER> DEFAULT_SOURCE_USER=<SOURCE_USER> DEFAULT_SOURCE_PASS=<SOURCE_PASS> DEFAULT_SOURCE_DB=<SOURCE_DB> DEFAULT_LOCAL_MYSQL_TYPE=docker DEFAULT_LOCAL_MYSQL_PORT=3306 DEFAULT_TARGET_CONTAINER=<TARGET_CONTAINER> DEFAULT_TARGET_USER=<TARGET_USER> DEFAULT_TARGET_PASS=<TARGET_PASS> DEFAULT_TARGET_DB=<TARGET_DB> DEFAULT_BACKUP_DIR=<BACKUP_DIR>
-
设置权限:
chmod 600 .env
2. 本地主机准备
-
安装依赖:
- Docker:
sudo apt-get update && sudo apt-get install -y docker.io openssh-client # Debian/Ubuntu sudo yum install -y docker && sudo systemctl start docker && systemctl enable docker # CentOS/RHEL
- 本机 MySQL:
sudo apt-get update && sudo apt-get install -y mysql-client # Debian/Ubuntu sudo yum install -y mysql # CentOS/RHEL
- Docker:
-
启动本地 MySQL:
- Docker:
docker run -d \ --name local_mysql \ -p 3306:3306 \ -e MYSQL_ROOT_PASSWORD=root_pass \ -e MYSQL_DATABASE=localdb \ mysql:latest
- 本机:
配置用户:sudo systemctl start mysql
非默认端口(如 3307):GRANT ALL PRIVILEGES ON localdb.* TO 'db_user'@'localhost' IDENTIFIED BY 'db_pass'; FLUSH PRIVILEGES;
修改sudo vim /etc/mysql/my.cnf
[mysqld]
:
重启:port = 3307
sudo systemctl restart mysql
- Docker:
-
创建备份目录:
mkdir -p /backup/mysql chmod 755 /backup/mysql
3. 远程服务器准备
-
安装依赖:
- Docker:
ssh user@192.168.1.100 "sudo apt-get update && sudo apt-get install -y docker.io"
- 本机 MySQL:
ssh user@192.168.1.100 "sudo apt-get update && sudo apt-get install -y mysql-server"
- Docker:
-
启动远程 MySQL:
- Docker:
ssh user@192.168.1.100 docker run -d \ --name remote_mysql \ -p 3306:3306 \ -e MYSQL_ROOT_PASSWORD=root_pass \ -e MYSQL_DATABASE=mydb \ mysql:latest
- 本机:
配置用户:ssh user@192.168.1.100 "sudo systemctl start mysql"
非默认端口(如 3307),修改GRANT ALL PRIVILEGES ON mydb.* TO 'db_user'@'%' IDENTIFIED BY 'db_pass'; FLUSH PRIVILEGES;
/etc/mysql/my.cnf
并重启:sudo systemctl restart mysql
- Docker:
-
配置 SSH 免密登录:
ssh-keygen ssh-copy-id user@192.168.1.100
4. 测试连接
-
本地数据库:
- Docker:
docker exec local_mysql mysql -u db_user --password=db_pass -e "SELECT 1"
- 本机:
mysql -u db_user --password=db_pass -P 3306 -e "SELECT 1"
- Docker:
-
远程数据库:
- Docker:
ssh user@192.168.1.100 "docker exec remote_mysql mysql -u db_user --password=db_pass -e 'SELECT 1'"
- 本机:
ssh user@192.168.1.100 "mysql -u db_user --password=db_pass -P 3306 -e 'SELECT 1'"
- Docker:
使用方法
-
保存脚本:
- 远程到本地:保存为
mysql_remote_to_local_sync.sh
。 - 本地到远程:保存为
mysql_local_to_remote_sync.sh
。 - 添加执行权限:
chmod +x mysql_remote_to_local_sync.sh mysql_local_to_remote_sync.sh
- 远程到本地:保存为
-
配置:
- 优先级:
.env
文件 > 环境变量 > 默认变量。 - 创建
.env
文件(见示例),或修改脚本中的DEFAULT_*
变量。 - 确保
<BACKUP_DIR>
存在且可写。
- 优先级:
-
运行脚本:
- 远程到本地:
./mysql_remote_to_local_sync.sh
- 本地到远程:
./mysql_local_to_remote_sync.sh
- 脚本会显示配置信息并提示确认:
>==============< |远程服务器信息| >==============< SSH用户名: REMOTE_USER=user 远程服务器IP: REMOTE_HOST=192.168.1.100 ... 请核实配置是否一致? [y/N]
- 输入
y
或yes
继续,否则退出。
- 远程到本地:
-
预期输出(示例):
[2025-06-04 20:56:00] 远程 mydb 数据库同步至本地 localdb 数据库 [2025-06-04 20:56:00] 开始数据库同步流程... [2025-06-04 20:56:00] 正在检查 LOCAL_MYSQL_TYPE REMOTE_MYSQL_TYPE .. [2025-06-04 20:56:00] 正在检查 LOCAL_MYSQL_TYPE:docker .. [2025-06-04 20:56:00] 正在检查 REMOTE_MYSQL_TYPE:docker .. [2025-06-04 20:56:01] 源数据库连接成功 [2025-06-04 20:56:02] 备份成功完成: /backup/mysql/mydb_20250604_205600.sql [2025-06-04 20:56:03] 同步成功完成 [2025-06-04 20:56:03] 已清理旧备份文件 [2025-06-04 20:56:03] 数据库同步成功完成
脚本代码
1. mysql_remote_to_local_sync.sh
#!/bin/bash
# 默认配置
DEFAULT_REMOTE_USER="<USER>" # SSH 用户名
DEFAULT_REMOTE_HOST="<IP>" # 远程服务器 IP
DEFAULT_REMOTE_MYSQL_TYPE="docker" # 远程 MySQL 类型: native 或 docker
DEFAULT_REMOTE_MYSQL_PORT=3306 # 远程 MySQL 端口(仅 native 模式,默认 3306)
DEFAULT_SOURCE_CONTAINER="<SOURCE_CONTAINER>" # 远程 Docker 容器名
DEFAULT_SOURCE_USER="<SOURCE_USER>" # 源数据库用户名
DEFAULT_SOURCE_PASS="<SOURCE_PASS>" # 源数据库密码
DEFAULT_SOURCE_DB="<SOURCE_DB>" # 源数据库名
DEFAULT_LOCAL_MYSQL_TYPE="docker" # 本地 MySQL 类型: native 或 docker
DEFAULT_LOCAL_MYSQL_PORT=3306 # 本地 MySQL 端口(仅 native 模式,默认 3306)
DEFAULT_TARGET_CONTAINER="<TARGET_CONTAINER>" # 本地 Docker 容器名
DEFAULT_TARGET_USER="<TARGET_USER>" # 目标数据库用户名
DEFAULT_TARGET_PASS="<TARGET_PASS>" # 目标数据库密码
DEFAULT_TARGET_DB="<TARGET_DB>" # 目标数据库名
DEFAULT_BACKUP_DIR="<BACKUP_DIR>" # 备份目录
# 加载现有 .env 文件
if [ -f ".env" ]; then
export $(cat .env | grep -v '^#' | xargs)
fi
# 远程服务器信息
REMOTE_USER=${REMOTE_USER:-$DEFAULT_REMOTE_USER}
REMOTE_HOST=${REMOTE_HOST:-$DEFAULT_REMOTE_HOST}
REMOTE_MYSQL_TYPE=${REMOTE_MYSQL_TYPE:-$DEFAULT_REMOTE_MYSQL_TYPE}
REMOTE_MYSQL_PORT=${REMOTE_MYSQL_PORT:-$DEFAULT_REMOTE_MYSQL_PORT}
SOURCE_CONTAINER=${SOURCE_CONTAINER:-$DEFAULT_SOURCE_CONTAINER}
SOURCE_USER=${SOURCE_USER:-$DEFAULT_SOURCE_USER}
SOURCE_PASS=${SOURCE_PASS:-$DEFAULT_SOURCE_PASS}
SOURCE_DB=${SOURCE_DB:-$DEFAULT_SOURCE_DB}
# 本地目标数据库信息
LOCAL_MYSQL_TYPE=${LOCAL_MYSQL_TYPE:-$DEFAULT_LOCAL_MYSQL_TYPE}
LOCAL_MYSQL_PORT=${LOCAL_MYSQL_PORT:-$DEFAULT_LOCAL_MYSQL_PORT}
TARGET_CONTAINER=${TARGET_CONTAINER:-$DEFAULT_TARGET_CONTAINER}
TARGET_USER=${TARGET_USER:-$DEFAULT_TARGET_USER}
TARGET_PASS=${TARGET_PASS:-$DEFAULT_TARGET_PASS}
TARGET_DB=${TARGET_DB:-$DEFAULT_TARGET_DB}
# 备份设置
BACKUP_DIR=${BACKUP_DIR:-$DEFAULT_BACKUP_DIR}
TIMESTAMP=$(date +%Y%m%d_%H%M%S)
# 检查必要的命令
check_required_commands() {
log "正在检查 LOCAL_MYSQL_TYPE REMOTE_MYSQL_TYPE .."
command -v ssh >/dev/null 2>&1 || { echo "需要安装 ssh,但未找到。程序中止。" >&2; exit 1; }
case "$LOCAL_MYSQL_TYPE" in
"docker")
log "正在检查 LOCAL_MYSQL_TYPE:$LOCAL_MYSQL_TYPE .."
command -v docker >/dev/null 2>&1 || { echo "本地 MySQL 类型为 docker,但未找到 docker 命令。程序中止。" >&2; exit 1; }
docker info >/dev/null 2>&1 || { echo "本地 Docker 服务未运行。程序中止。" >&2; exit 1; }
docker exec "$TARGET_CONTAINER" sh -c "command -v mysql >/dev/null" 2>&1 || { echo "本地 Docker 容器 $TARGET_CONTAINER 未找到 mysql 命令。程序中止。" >&2; exit 1; }
docker exec "$TARGET_CONTAINER" sh -c "command -v mysqldump >/dev/null" 2>&1 || { echo "本地 Docker 容器 $TARGET_CONTAINER 未找到 mysqldump。程序中止。" >&2; exit 1; }
;;
"native")
command -v mysql >/dev/null 2>&1 || { echo "本地 MySQL 类型为 native,但未找到 mysql 命令。程序中止。" >&2; exit 1; }
command -v mysqldump >/dev/null 2>&1 || { echo "本地 MySQL 类型为 native,但未找到 mysqldump 命令。程序中止。" >&2; exit 1; }
;;
*)
echo "本地 MySQL 类型 LOCAL_MYSQL_TYPE 必须为 'native' 或 'docker',当前值为:$LOCAL_MYSQL_TYPE。程序中止。" >&2; exit 1;
;;
esac
case "$REMOTE_MYSQL_TYPE" in
"docker")
log "正在检查 REMOTE_MYSQL_TYPE:$REMOTE_MYSQL_TYPE .."
command -v docker >/dev/null 2>&1 || { echo "远程 MySQL 类型为 docker,但未找到 docker 命令。程序中止。" >&2; exit 1; }
docker info >/dev/null 2>&1 || { echo "本地 Docker 服务未运行(需要本地 docker 命令检查远程容器)。程序中止。" >&2; exit 1; }
ssh "$REMOTE_USER@$REMOTE_HOST" "docker exec $SOURCE_CONTAINER sh -c 'command -v mysql >/dev/null'" >/dev/null 2>&1 || { echo "远程 Docker 容器 $SOURCE_CONTAINER 未找到 mysql 命令。程序中止。" >&2; exit 1; }
ssh "$REMOTE_USER@$REMOTE_HOST" "docker exec $SOURCE_CONTAINER sh -c 'command -v mysqldump >/dev/null'" >/dev/null 2>&1 || { echo "远程 Docker 容器 $SOURCE_CONTAINER 未找到 mysqldump 命令。程序中止。" >&2; exit 1; }
;;
"native")
ssh "$REMOTE_USER@$REMOTE_HOST" "command -v mysql >/dev/null" >/dev/null 2>&1 || { echo "远程 MySQL 类型为 native,但未找到 mysql 命令。程序中止。" >&2; exit 1; }
ssh "$REMOTE_USER@$REMOTE_HOST" "command -v mysqldump >/dev/null" >/dev/null 2>&1 || { echo "远程 MySQL 类型为 native,但未找到 mysqldump 命令。程序中止。" >&2; exit 1; }
;;
*)
echo "远程 MySQL 类型 REMOTE_MYSQL_TYPE 必须为 'native' 或 'docker',当前值为:$REMOTE_MYSQL_TYPE。程序中止。" >&2; exit 1;
;;
esac
}
# 创建备份目录
mkdir -p "$BACKUP_DIR"
# 日志记录函数
log() {
echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1"
}
# 检查容器和连接
check_connection() {
if [ "$REMOTE_MYSQL_TYPE" = "docker" ]; then
log "正在检查 $REMOTE_HOST 上的源容器 ($SOURCE_CONTAINER)..."
ssh "$REMOTE_USER@$REMOTE_HOST" "docker ps | grep $SOURCE_CONTAINER" >/dev/null
if [ $? -ne 0 ]; then
log "$REMOTE_HOST 上的源容器 $SOURCE_CONTAINER 未运行"
exit 1
else
log "$REMOTE_HOST 上的源容器 $SOURCE_CONTAINER 已运行"
fi
fi
log "正在测试源数据库连接..."
if [ "$REMOTE_MYSQL_TYPE" = "docker" ]; then
ssh "$REMOTE_USER@$REMOTE_HOST" "docker exec $SOURCE_CONTAINER mysql -u $SOURCE_USER --password=$SOURCE_PASS -e 'SELECT 1'" >/dev/null 2>&1
else
ssh "$REMOTE_USER@$REMOTE_HOST" "mysql -u $SOURCE_USER --password=$SOURCE_PASS -P $REMOTE_MYSQL_PORT -e 'SELECT 1'" >/dev/null 2>&1
fi
if [ $? -ne 0 ]; then
log "源数据库连接失败"
exit 1
else
log "源数据库连接成功"
fi
if [ "$LOCAL_MYSQL_TYPE" = "docker" ]; then
log "正在检查本地目标容器 ($TARGET_CONTAINER)..."
docker ps | grep "$TARGET_CONTAINER" >/dev/null
if [ $? -ne 0 ]; then
log "本地目标容器 $TARGET_CONTAINER 未运行"
exit 1
else
log "本地目标容器 $TARGET_CONTAINER 已运行"
fi
fi
log "正在测试目标数据库连接..."
if [ "$LOCAL_MYSQL_TYPE" = "docker" ]; then
docker exec "$TARGET_CONTAINER" mysql -u "$TARGET_USER" --password="$TARGET_PASS" -e "SELECT 1" >/dev/null 2>&1
else
mysql -u "$TARGET_USER" --password="$TARGET_PASS" -P $LOCAL_MYSQL_PORT -e "SELECT 1" >/dev/null 2>&1
fi
if [ $? -ne 0 ]; then
log "目标数据库连接失败"
exit 1
else
log "目标数据库连接成功"
fi
}
# 备份源数据库(远程)
backup_source() {
BACKUP_FILE="$BACKUP_DIR/${SOURCE_DB}_${TIMESTAMP}.sql"
log "开始从 $REMOTE_HOST 备份源数据库..."
if [ "$REMOTE_MYSQL_TYPE" = "docker" ]; then
ssh "$REMOTE_USER@$REMOTE_HOST" \
"docker exec $SOURCE_CONTAINER mysqldump -u $SOURCE_USER --password=$SOURCE_PASS \
--single-transaction --routines --triggers $SOURCE_DB" > "$BACKUP_FILE" 2>/dev/null
else
ssh "$REMOTE_USER@$REMOTE_HOST" \
"mysqldump -u $SOURCE_USER --password=$SOURCE_PASS -P $REMOTE_MYSQL_PORT \
--single-transaction --routines --triggers $SOURCE_DB" > "$BACKUP_FILE" 2>/dev/null
fi
if [ $? -eq 0 ]; then
if [ -s "$BACKUP_FILE" ] && grep -q "^-- MySQL dump" "$BACKUP_FILE"; then
log "备份成功完成: $BACKUP_FILE"
else
log "备份文件无效或为空!"
exit 1
fi
else
log "备份失败!"
exit 1
fi
}
# 同步到目标数据库(本地)
sync_to_target() {
log "开始同步到目标数据库..."
if [ "$LOCAL_MYSQL_TYPE" = "docker" ]; then
cat "$BACKUP_FILE" | docker exec -i "$TARGET_CONTAINER" mysql -u "$TARGET_USER" --password="$TARGET_PASS" "$TARGET_DB" >/dev/null 2>&1
else
cat "$BACKUP_FILE" | mysql -u "$TARGET_USER" --password="$TARGET_PASS" -P $LOCAL_MYSQL_PORT "$TARGET_DB" >/dev/null 2>&1
fi
if [ $? -eq 0 ]; then
log "同步成功完成"
else
log "同步失败!"
exit 1
fi
}
# 主执行流程
main() {
log "远程$SOURCE_DB数据库同步至本地$TARGET_DB数据库"
log "开始数据库同步流程..."
# 打印配置信息并要求用户确认
echo "
>==============<
|远程服务器信息|
>==============<
SSH用户名:
REMOTE_USER=$REMOTE_USER
远程服务器IP:
REMOTE_HOST=$REMOTE_HOST
远程 MySQL类型(native或 docker):
REMOTE_MYSQL_TYPE=$REMOTE_MYSQL_TYPE
远程 MySQL端口(仅 native模式,默认3306):
REMOTE_MYSQL_PORT=$REMOTE_MYSQL_PORT
远程 Docker容器名:
SOURCE_CONTAINER=$SOURCE_CONTAINER
源数据库用户名:
SOURCE_USER=$SOURCE_USER
源数据库密码:
SOURCE_PASS=$SOURCE_PASS
源数据库名:
SOURCE_DB=$SOURCE_DB
>===================<
|本地目标数据库信息|
>===================<
本地 MySQL类型(native或 docker):
LOCAL_MYSQL_TYPE=$LOCAL_MYSQL_TYPE
本地 MySQL端口(仅 native模式,默认3306):
LOCAL_MYSQL_PORT=$LOCAL_MYSQL_PORT
本地 Docker容器名:
TARGET_CONTAINER=$TARGET_CONTAINER
目标数据库用户名:
TARGET_USER=$TARGET_USER
目标数据库密码:
TARGET_PASS=$TARGET_PASS
目标数据库名:
TARGET_DB=$TARGET_DB
备份设置:
BACKUP_DIR=$BACKUP_DIR
"
read -r -p "请核实配置是否一致? [y/N] " key
if [[ "${key,,}" =~ ^(yes|y)$ ]]; then
echo ""
else
log "用户取消操作,程序退出"
exit 0
fi
# 检查命令
check_required_commands
# 检查连接
check_connection
# 执行备份
backup_source
# 执行同步
sync_to_target
# 清理旧的备份文件(保留最近5个)
cd "$BACKUP_DIR" || exit
ls -t | grep "${SOURCE_DB}_.*\.sql" | tail -n +6 | xargs -I {} rm {} 2>/dev/null
log "已清理旧备份文件"
log "数据库同步成功完成"
}
# 错误处理
set -e
trap 'log "发生错误,程序退出..."; exit 1' ERR
# 执行主函数
main
exit 0
2. mysql_local_to_remote_sync.sh
#!/bin/bash
# 默认配置
DEFAULT_LOCAL_MYSQL_TYPE="docker" # 本地 MySQL 类型: native 或 docker
DEFAULT_LOCAL_MYSQL_PORT=3306 # 本地 MySQL 端口(仅 native 模式,默认 3306)
DEFAULT_SOURCE_CONTAINER="<SOURCE_CONTAINER>" # 本地 Docker 容器名
DEFAULT_SOURCE_USER="<SOURCE_USER>" # 源数据库用户名
DEFAULT_SOURCE_PASS="<SOURCE_PASS>" # 源数据库密码
DEFAULT_SOURCE_DB="<SOURCE_DB>" # 源数据库名
DEFAULT_REMOTE_USER="<USER>" # SSH 用户名
DEFAULT_REMOTE_HOST="<IP>" # 远程服务器 IP
DEFAULT_REMOTE_MYSQL_TYPE="docker" # 远程 MySQL 类型: native 或 docker
DEFAULT_REMOTE_MYSQL_PORT=3306 # 远程 MySQL 端口(仅 native 模式,默认 3306)
DEFAULT_TARGET_CONTAINER="<TARGET_CONTAINER>" # 远程 Docker 容器名
DEFAULT_TARGET_USER="<TARGET_USER>" # 目标数据库用户名
DEFAULT_TARGET_PASS="<TARGET_PASS>" # 目标数据库密码
DEFAULT_TARGET_DB="<TARGET_DB>" # 目标数据库名
DEFAULT_BACKUP_DIR="<BACKUP_DIR>" # 备份目录
# 加载现有 .env 文件
if [ -f ".env" ]; then
export $(cat .env | grep -v '^#' | xargs)
fi
# 本地源数据库信息
LOCAL_MYSQL_TYPE=${LOCAL_MYSQL_TYPE:-$DEFAULT_LOCAL_MYSQL_TYPE}
LOCAL_MYSQL_PORT=${LOCAL_MYSQL_PORT:-$DEFAULT_LOCAL_MYSQL_PORT}
SOURCE_CONTAINER=${SOURCE_CONTAINER:-$DEFAULT_SOURCE_CONTAINER}
SOURCE_USER=${SOURCE_USER:-$DEFAULT_SOURCE_USER}
SOURCE_PASS=${SOURCE_PASS:-$DEFAULT_SOURCE_PASS}
SOURCE_DB=${SOURCE_DB:-$DEFAULT_SOURCE_DB}
# 远程目标数据库信息
REMOTE_USER=${REMOTE_USER:-$DEFAULT_REMOTE_USER}
REMOTE_HOST=${REMOTE_HOST:-$DEFAULT_REMOTE_HOST}
REMOTE_MYSQL_TYPE=${REMOTE_MYSQL_TYPE:-$DEFAULT_REMOTE_MYSQL_TYPE}
REMOTE_MYSQL_PORT=${REMOTE_MYSQL_PORT:-$DEFAULT_REMOTE_MYSQL_PORT}
TARGET_CONTAINER=${TARGET_CONTAINER:-$DEFAULT_TARGET_CONTAINER}
TARGET_USER=${TARGET_USER:-$DEFAULT_TARGET_USER}
TARGET_PASS=${TARGET_PASS:-$DEFAULT_TARGET_PASS}
TARGET_DB=${TARGET_DB:-$DEFAULT_TARGET_DB}
# 备份设置
BACKUP_DIR=${BACKUP_DIR:-$DEFAULT_BACKUP_DIR}
TIMESTAMP=$(date +%Y%m%d_%H%M%S)
# 检查必要的命令
check_required_commands() {
log "正在检查 LOCAL_MYSQL_TYPE REMOTE_MYSQL_TYPE .."
command -v ssh >/dev/null 2>&1 || { echo "需要安装 ssh,但未找到。程序中止。" >&2; exit 1; }
case "$LOCAL_MYSQL_TYPE" in
"docker")
log "正在检查 LOCAL_MYSQL_TYPE:$LOCAL_MYSQL_TYPE .."
command -v docker >/dev/null 2>&1 || { echo "本地 MySQL 类型为 docker,但未找到 docker 命令。程序中止。" >&2; exit 1; }
docker info >/dev/null 2>&1 || { echo "本地 Docker 服务未运行。程序中止。" >&2; exit 1; }
docker exec "$SOURCE_CONTAINER" sh -c "command -v mysql >/dev/null" 2>&1 || { echo "本地 Docker 容器 $SOURCE_CONTAINER 未找到 mysql 命令。程序中止。" >&2; exit 1; }
docker exec "$SOURCE_CONTAINER" sh -c "command -v mysqldump >/dev/null" 2>&1 || { echo "本地 Docker 容器 $SOURCE_CONTAINER 未找到 mysqldump 命令。程序中止。" >&2; exit 1; }
;;
"native")
command -v mysql >/dev/null 2>&1 || { echo "本地 MySQL 类型为 native,但未找到 mysql 命令。程序中止。" >&2; exit 1; }
command -v mysqldump >/dev/null 2>&1 || { echo "本地 MySQL 类型为 native,但未找到 mysqldump 命令。程序中止。" >&2; exit 1; }
;;
*)
echo "本地 MySQL 类型 LOCAL_MYSQL_TYPE 必须为 'native' 或 'docker',当前值为:$LOCAL_MYSQL_TYPE。程序中止。" >&2; exit 1;
;;
esac
case "$REMOTE_MYSQL_TYPE" in
"docker")
log "正在检查 REMOTE_MYSQL_TYPE:$REMOTE_MYSQL_TYPE .."
command -v docker >/dev/null 2>&1 || { echo "远程 MySQL 类型为 docker,但未找到 docker 命令。程序中止。" >&2; exit 1; }
docker info >/dev/null 2>&1 || { echo "本地 Docker 服务未运行(需要本地 docker 命令检查远程容器)。程序中止。" >&2; exit 1; }
ssh "$REMOTE_USER@$REMOTE_HOST" "docker exec $TARGET_CONTAINER sh -c 'command -v mysql >/dev/null'" >/dev/null 2>&1 || { echo "远程 Docker 容器 $TARGET_CONTAINER 未找到 mysql 命令。程序中止。" >&2; exit 1; }
ssh "$REMOTE_USER@$REMOTE_HOST" "docker exec $TARGET_CONTAINER sh -c 'command -v mysqldump >/dev/null'" >/dev/null 2>&1 || { echo "远程 Docker 容器 $TARGET_CONTAINER 未找到 mysqldump 命令。程序中止。" >&2; exit 1; }
;;
"native")
ssh "$REMOTE_USER@$REMOTE_HOST" "command -v mysql >/dev/null" >/dev/null 2>&1 || { echo "远程 MySQL 类型为 native,但未找到 mysql 命令。程序中止。" >&2; exit 1; }
ssh "$REMOTE_USER@$REMOTE_HOST" "command -v mysqldump >/dev/null" >/dev/null 2>&1 || { echo "远程 MySQL 类型为 native,但未找到 mysqldump 命令。程序中止。" >&2; exit 1; }
;;
*)
echo "远程 MySQL 类型 REMOTE_MYSQL_TYPE 必须为 'native' 或 'docker',当前值为:$REMOTE_MYSQL_TYPE。程序中止。" >&2; exit 1;
;;
esac
}
# 创建备份目录
mkdir -p "$BACKUP_DIR"
# 日志记录函数
log() {
echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1"
}
# 检查容器和连接
check_connection() {
if [ "$LOCAL_MYSQL_TYPE" = "docker" ]; then
log "正在检查本地源容器 ($SOURCE_CONTAINER)..."
docker ps | grep "$SOURCE_CONTAINER" >/dev/null
if [ $? -ne 0 ]; then
log "本地源容器 $SOURCE_CONTAINER 未运行"
exit 1
else
log "本地源容器 $SOURCE_CONTAINER 已运行"
fi
fi
log "正在测试源数据库连接..."
if [ "$LOCAL_MYSQL_TYPE" = "docker" ]; then
docker exec "$SOURCE_CONTAINER" mysql -u "$SOURCE_USER" --password="$SOURCE_PASS" -e "SELECT 1" >/dev/null 2>&1
else
mysql -u "$SOURCE_USER" --password="$SOURCE_PASS" -P $LOCAL_MYSQL_PORT -e "SELECT 1" >/dev/null 2>&1
fi
if [ $? -ne 0 ]; then
log "源数据库连接失败"
exit 1
else
log "源数据库连接成功"
fi
if [ "$REMOTE_MYSQL_TYPE" = "docker" ]; then
log "正在检查 $REMOTE_HOST 上的目标容器 ($TARGET_CONTAINER)..."
ssh "$REMOTE_USER@$REMOTE_HOST" "docker ps | grep $TARGET_CONTAINER" >/dev/null
if [ $? -ne 0 ]; then
log "$REMOTE_HOST 上的目标容器 $TARGET_CONTAINER 未运行"
exit 1
else
log "$REMOTE_HOST 上的目标容器 $TARGET_CONTAINER 已运行"
fi
fi
log "正在测试目标数据库连接..."
if [ "$REMOTE_MYSQL_TYPE" = "docker" ]; then
ssh "$REMOTE_USER@$REMOTE_HOST" "docker exec $TARGET_CONTAINER mysql -u $TARGET_USER --password=$TARGET_PASS -e 'SELECT 1'" >/dev/null 2>&1
else
ssh "$REMOTE_USER@$REMOTE_HOST" "mysql -u $TARGET_USER --password=$TARGET_PASS -P $REMOTE_MYSQL_PORT -e 'SELECT 1'" >/dev/null 2>&1
fi
if [ $? -ne 0 ]; then
log "目标数据库连接失败"
exit 1
else
log "目标数据库连接成功"
fi
}
# 备份源数据库(本地)
backup_source() {
BACKUP_FILE="$BACKUP_DIR/${SOURCE_DB}_${TIMESTAMP}.sql"
log "开始从本地备份源数据库..."
if [ "$LOCAL_MYSQL_TYPE" = "docker" ]; then
docker exec "$SOURCE_CONTAINER" mysqldump -u "$SOURCE_USER" --password="$SOURCE_PASS" \
--single-transaction --routines --triggers "$SOURCE_DB" > "$BACKUP_FILE" 2>/dev/null
else
mysqldump -u "$SOURCE_USER" --password="$SOURCE_PASS" -P $LOCAL_MYSQL_PORT \
--single-transaction --routines --triggers "$SOURCE_DB" > "$BACKUP_FILE" 2>/dev/null
fi
if [ $? -eq 0 ]; then
if [ -s "$BACKUP_FILE" ] && grep -q "^-- MySQL dump" "$BACKUP_FILE"; then
log "备份成功完成: $BACKUP_FILE"
else
log "备份文件无效或为空!"
exit 1
fi
else
log "备份失败!"
exit 1
fi
}
# 同步到目标数据库(远程)
sync_to_target() {
log "开始同步到目标数据库..."
if [ "$REMOTE_MYSQL_TYPE" = "docker" ]; then
cat "$BACKUP_FILE" | ssh "$REMOTE_USER@$REMOTE_HOST" \
"docker exec -i $TARGET_CONTAINER mysql -u $TARGET_USER --password=$TARGET_PASS $TARGET_DB" >/dev/null 2>&1
else
cat "$BACKUP_FILE" | ssh "$REMOTE_USER@$REMOTE_HOST" \
"mysql -u $TARGET_USER --password=$TARGET_PASS -P $REMOTE_MYSQL_PORT $TARGET_DB" >/dev/null 2>&1
fi
if [ $? -eq 0 ]; then
log "同步成功完成"
else
log "同步失败!"
exit 1
fi
}
# 主执行流程
main() {
log "本地 $SOURCE_DB 数据库同步至远程 $TARGET_DB 数据库"
log "开始数据库同步流程..."
# 打印配置信息并要求用户确认
echo "
>==============<
|远程服务器信息|
>==============<
SSH用户名:
REMOTE_USER=$REMOTE_USER
远程服务器IP:
REMOTE_HOST=$REMOTE_HOST
远程 MySQL类型(native或 docker):
REMOTE_MYSQL_TYPE=$REMOTE_MYSQL_TYPE
远程 MySQL端口(仅 native模式,默认3306):
REMOTE_MYSQL_PORT=$REMOTE_MYSQL_PORT
远程 Docker容器名:
TARGET_CONTAINER=$TARGET_CONTAINER
目标数据库用户名:
TARGET_USER=$TARGET_USER
目标数据库密码:
TARGET