#!/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} 个"