160 lines
6.2 KiB
Bash
160 lines
6.2 KiB
Bash
#!/usr/bin/env bash
|
||
# ============================================================
|
||
# dump_mysql_schema.sh
|
||
# ------------------------------------------------------------
|
||
# 导出 MySQL 整库(或指定库)的纯 DDL 到 stdout。
|
||
# 目的:把线上生产数据库的"表结构"抓回来,与 docs/sql/init_database.sql 做 diff。
|
||
#
|
||
# 适用场景(任选其一):
|
||
# 1) 宿主机直接执行(远程 MySQL 同样可用,只要本机能连过去)
|
||
# 2) 容器内执行:
|
||
# docker exec -i txw-mysql bash -s < devops/dump_mysql_schema.sh \
|
||
# > prod_schema_$(date +%Y%m%d).sql
|
||
#
|
||
# 用法示例(远程库):
|
||
# MYSQL_HOST=192.168.110.55 MYSQL_PORT=3306 \
|
||
# MYSQL_USER=root MYSQL_PWD='your_password' \
|
||
# INCLUDE_DBS=qyd_txw \
|
||
# bash devops/dump_mysql_schema.sh > qyd_txw_schema_$(date +%Y%m%d).sql
|
||
#
|
||
# 可调环境变量(全部可选):
|
||
# MYSQL_HOST 远端/本机地址(默认 localhost)
|
||
# MYSQL_PORT 端口(默认 3306)
|
||
# MYSQL_USER 用户名(默认 root)
|
||
# MYSQL_PWD 密码(推荐用单引号包裹,避免 $、! 等被 bash 解释)
|
||
# INCLUDE_DBS 只导指定库,多个用逗号分隔;留空=全部业务库
|
||
# EXCLUDE_DBS 排除的库(默认:information_schema,performance_schema,mysql,sys)
|
||
# ============================================================
|
||
|
||
set -euo pipefail
|
||
|
||
# ---------- 默认值(与 devops/docker-compose.infra.prod.yml 保持一致) ----------
|
||
MYSQL_USER="${MYSQL_USER:-root}"
|
||
MYSQL_PWD="${MYSQL_PWD:-MysqlRootPwd@2024#Secure}"
|
||
MYSQL_HOST="${MYSQL_HOST:-localhost}"
|
||
MYSQL_PORT="${MYSQL_PORT:-3306}"
|
||
EXCLUDE_DBS="${EXCLUDE_DBS:-information_schema,performance_schema,mysql,sys}"
|
||
INCLUDE_DBS="${INCLUDE_DBS:-}"
|
||
|
||
# 容器内不落盘:所有 SQL 走 stdout 流到宿主机,容器内零残留。
|
||
|
||
# ---------- 工具自检 ----------
|
||
for bin in mysql mysqldump; do
|
||
if ! command -v "$bin" >/dev/null 2>&1; then
|
||
echo "[FATAL] 当前环境找不到 $bin,请先安装 mysql-client(apt: default-mysql-client / yum: mysql / dnf: mysql)" >&2
|
||
exit 1
|
||
fi
|
||
done
|
||
|
||
# 把密码通过 MYSQL_PWD 环境变量传给客户端,避免命令行泄露
|
||
export MYSQL_PWD
|
||
|
||
log() { echo "[$(date '+%H:%M:%S')] $*" >&2; }
|
||
fail() { echo "[FATAL] $*" >&2; exit 1; }
|
||
|
||
# ---------- 1. 连通性自检 ----------
|
||
log "自检:尝试连接 ${MYSQL_USER}@${MYSQL_HOST}:${MYSQL_PORT} ..."
|
||
if ! mysql -h "$MYSQL_HOST" -P "$MYSQL_PORT" -u "$MYSQL_USER" \
|
||
-e "SELECT VERSION();" >/dev/null 2>&1; then
|
||
fail "无法连接 MySQL,请检查账号/密码/网络"
|
||
fi
|
||
VERSION=$(mysql -h "$MYSQL_HOST" -P "$MYSQL_PORT" -u "$MYSQL_USER" -N -B \
|
||
-e "SELECT VERSION();" 2>/dev/null)
|
||
log "连接成功,MySQL 版本: ${VERSION}"
|
||
|
||
# ---------- 2. 列出要导出的库 ----------
|
||
if [ -n "$INCLUDE_DBS" ]; then
|
||
# 把逗号分隔的 INCLUDE_DBS 变成 SQL IN (...) 列表
|
||
IN_CLAUSE=$(printf "%s" "$INCLUDE_DBS" | awk -F',' '{
|
||
out=""; for (i=1;i<=NF;i++){ gsub(/^ +| +$/,"",$i); out=out"'\''"$i"'\''," }
|
||
sub(/,$/,"",out); print out
|
||
}')
|
||
DB_LIST=$(mysql -h "$MYSQL_HOST" -P "$MYSQL_PORT" -u "$MYSQL_USER" -N -B -e \
|
||
"SELECT SCHEMA_NAME FROM information_schema.SCHEMATA
|
||
WHERE SCHEMA_NAME IN ($IN_CLAUSE) ORDER BY SCHEMA_NAME;" 2>/dev/null)
|
||
else
|
||
# 用 NOT REGEXP 排除系统库
|
||
EXCLUDED_REGEX=$(printf "%s" "$EXCLUDE_DBS" | sed 's/,/|/g')
|
||
DB_LIST=$(mysql -h "$MYSQL_HOST" -P "$MYSQL_PORT" -u "$MYSQL_USER" -N -B -e \
|
||
"SELECT SCHEMA_NAME FROM information_schema.SCHEMATA
|
||
WHERE SCHEMA_NAME NOT REGEXP '$EXCLUDED_REGEX'
|
||
ORDER BY SCHEMA_NAME;" 2>/dev/null)
|
||
fi
|
||
|
||
if [ -z "$DB_LIST" ]; then
|
||
fail "未找到任何目标库(INCLUDE_DBS='${INCLUDE_DBS}', EXCLUDE_DBS='${EXCLUDE_DBS}')"
|
||
fi
|
||
|
||
log "将导出以下库: $(echo $DB_LIST | tr '\n' ' ')"
|
||
|
||
# ---------- 3. 写文件头(直写 stdout,不落盘) ----------
|
||
echo "-- ============================================================"
|
||
echo "-- MySQL 整库 DDL 导出"
|
||
echo "-- 数据库版本: ${VERSION}"
|
||
echo "-- 生成时间 : $(date '+%Y-%m-%d %H:%M:%S %z')"
|
||
echo "-- 目标库 : $(echo $DB_LIST | tr '\n' ',' | sed 's/,$//')"
|
||
echo "-- 生成方式 : mysqldump --no-data(仅结构,不含数据)"
|
||
echo "-- ============================================================"
|
||
echo "SET NAMES utf8mb4;"
|
||
echo "SET FOREIGN_KEY_CHECKS = 0;"
|
||
|
||
# ---------- 4. 逐库 dump(直接 echo 到 stdout) ----------
|
||
TOTAL_TABLES=0
|
||
TOTAL_VIEWS=0
|
||
|
||
for DB in $DB_LIST; do
|
||
log "==> 导出库 [$DB]"
|
||
|
||
echo ""
|
||
echo "-- ----------------------------"
|
||
echo "-- Database: ${DB}"
|
||
echo "-- ----------------------------"
|
||
echo "USE \`${DB}\`;"
|
||
|
||
# 取表列表
|
||
TABLES=$(mysql -h "$MYSQL_HOST" -P "$MYSQL_PORT" -u "$MYSQL_USER" -N -B -e \
|
||
"SELECT TABLE_NAME FROM information_schema.TABLES
|
||
WHERE TABLE_SCHEMA='${DB}' AND TABLE_TYPE='BASE TABLE'
|
||
ORDER BY TABLE_NAME;" 2>/dev/null)
|
||
|
||
for TBL in $TABLES; do
|
||
echo "DROP TABLE IF EXISTS \`${TBL}\`;"
|
||
mysqldump -h "$MYSQL_HOST" -P "$MYSQL_PORT" -u "$MYSQL_USER" \
|
||
--no-data --skip-comments --skip-add-locks \
|
||
--skip-lock-tables --skip-disable-keys \
|
||
--skip-tz-utc --set-gtid-purged=OFF \
|
||
--default-character-set=utf8mb4 \
|
||
--compact --skip-add-drop-table \
|
||
"${DB}" "${TBL}" 2>/dev/null
|
||
echo ";"
|
||
echo ""
|
||
TOTAL_TABLES=$((TOTAL_TABLES + 1))
|
||
done
|
||
|
||
# 取视图列表
|
||
VIEWS=$(mysql -h "$MYSQL_HOST" -P "$MYSQL_PORT" -u "$MYSQL_USER" -N -B -e \
|
||
"SELECT TABLE_NAME FROM information_schema.VIEWS
|
||
WHERE TABLE_SCHEMA='${DB}' ORDER BY TABLE_NAME;" 2>/dev/null || true)
|
||
|
||
for VW in $VIEWS; do
|
||
echo "DROP VIEW IF EXISTS \`${VW}\`;"
|
||
mysqldump -h "$MYSQL_HOST" -P "$MYSQL_PORT" -u "$MYSQL_USER" \
|
||
--no-data --skip-comments --skip-add-locks \
|
||
--skip-lock-tables --skip-disable-keys \
|
||
--set-gtid-purged=OFF \
|
||
--default-character-set=utf8mb4 \
|
||
--compact --skip-add-drop-table \
|
||
"${DB}" "${VW}" 2>/dev/null
|
||
echo ";"
|
||
echo ""
|
||
TOTAL_VIEWS=$((TOTAL_VIEWS + 1))
|
||
done
|
||
done
|
||
|
||
# ---------- 5. 文件尾 ----------
|
||
echo ""
|
||
echo "SET FOREIGN_KEY_CHECKS = 1;"
|
||
|
||
# 统计日志走 stderr,不会污染 stdout 上的 SQL 文件
|
||
log "导出完成:表 ${TOTAL_TABLES} 个,视图 ${TOTAL_VIEWS} 个"
|