89 lines
3.1 KiB
Bash
89 lines
3.1 KiB
Bash
|
|
#!/usr/bin/env bash
|
|||
|
|
# 恢复演练 —— 把最近一份备份恢复到「临时库」并核对行数(⑨:备份不演练 = 没有备份)
|
|||
|
|
#
|
|||
|
|
# 演练绝不直接覆盖生产库;临时库名 payroll_drill_<时间戳>,核对完由
|
|||
|
|
# 调用方决定是否删除(本脚本默认不 DROP,安全优先)。
|
|||
|
|
#
|
|||
|
|
# 用法:在项目根目录执行 bash scripts/restore_drill.sh
|
|||
|
|
# 产物:临时库 + 终端行数对比报告;--drop-exec 参数在核对通过后删除临时库。
|
|||
|
|
|
|||
|
|
set -euo pipefail
|
|||
|
|
|
|||
|
|
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
|||
|
|
cd "$ROOT"
|
|||
|
|
|
|||
|
|
if [[ -f docker/.env ]]; then
|
|||
|
|
# shellcheck disable=SC1091
|
|||
|
|
source docker/.env
|
|||
|
|
else
|
|||
|
|
echo "ERROR: docker/.env 不存在" >&2
|
|||
|
|
exit 1
|
|||
|
|
fi
|
|||
|
|
|
|||
|
|
: "${MYSQL_DATABASE:?docker/.env 缺 MYSQL_DATABASE}"
|
|||
|
|
: "${MYSQL_ROOT_PASSWORD:?docker/.env 缺 MYSQL_ROOT_PASSWORD}"
|
|||
|
|
|
|||
|
|
CONTAINER="ys-mysql-dev"
|
|||
|
|
if ! docker inspect "$CONTAINER" >/dev/null 2>&1; then
|
|||
|
|
echo "ERROR: 容器 $CONTAINER 未运行" >&2
|
|||
|
|
exit 1
|
|||
|
|
fi
|
|||
|
|
|
|||
|
|
BACKUP_DIR="$ROOT/db/backups"
|
|||
|
|
LATEST="$(ls -t "$BACKUP_DIR"/payroll_*.sql.gz 2>/dev/null | head -1)"
|
|||
|
|
if [[ -z "$LATEST" ]]; then
|
|||
|
|
echo "ERROR: 未找到备份文件(先 bash scripts/backup_full.sh)" >&2
|
|||
|
|
exit 1
|
|||
|
|
fi
|
|||
|
|
|
|||
|
|
DRILL_DB="payroll_drill_$(date +%Y%m%d_%H%M%S)"
|
|||
|
|
echo "演练恢复:$(basename "$LATEST") → 临时库 $DRILL_DB"
|
|||
|
|
|
|||
|
|
# 1. 创建临时库(mysqladmin create 无反引号转义问题)
|
|||
|
|
docker exec "$CONTAINER" sh -c \
|
|||
|
|
"mysqladmin -uroot -p\"\$MYSQL_ROOT_PASSWORD\" create \"$DRILL_DB\""
|
|||
|
|
|
|||
|
|
# 2. 解压导入
|
|||
|
|
gunzip -c "$LATEST" | docker exec -i "$CONTAINER" sh -c \
|
|||
|
|
"mysql -uroot -p\"\$MYSQL_ROOT_PASSWORD\" \"$DRILL_DB\""
|
|||
|
|
|
|||
|
|
# 3. 行数核对:关键业务表
|
|||
|
|
TABLES=(users roles user_roles param_definitions param_values businesses business_items personnel products customers shipments shipment_items receipts approval_orders approval_steps system_settings)
|
|||
|
|
|
|||
|
|
echo ""
|
|||
|
|
echo "======== 行数对比(源库 vs 演练库)========"
|
|||
|
|
printf "%-24s %12s %12s %6s\n" "表名" "源库" "演练库" "结果"
|
|||
|
|
ALL_OK=1
|
|||
|
|
for T in "${TABLES[@]}"; do
|
|||
|
|
SRC=$(docker exec "$CONTAINER" sh -c \
|
|||
|
|
"mysql -uroot -p\"\$MYSQL_ROOT_PASSWORD\" -N -B -e \"SELECT COUNT(*) FROM $MYSQL_DATABASE.$T;\" 2>/dev/null" || echo "?")
|
|||
|
|
DST=$(docker exec "$CONTAINER" sh -c \
|
|||
|
|
"mysql -uroot -p\"\$MYSQL_ROOT_PASSWORD\" -N -B -e \"SELECT COUNT(*) FROM $DRILL_DB.$T;\" 2>/dev/null" || echo "?")
|
|||
|
|
if [[ "$SRC" == "$DST" ]]; then
|
|||
|
|
MARK="✓"
|
|||
|
|
else
|
|||
|
|
MARK="✗"
|
|||
|
|
ALL_OK=0
|
|||
|
|
fi
|
|||
|
|
printf "%-24s %12s %12s %6s\n" "$T" "$SRC" "$DST" "$MARK"
|
|||
|
|
done
|
|||
|
|
echo "=========================================="
|
|||
|
|
|
|||
|
|
if [[ "$ALL_OK" == "1" ]]; then
|
|||
|
|
echo ""
|
|||
|
|
echo "结果:全部表行数一致,备份可恢复。"
|
|||
|
|
if [[ "${1:-}" == "--drop-exec" ]]; then
|
|||
|
|
docker exec "$CONTAINER" sh -c \
|
|||
|
|
"mysqladmin -uroot -p\"\$MYSQL_ROOT_PASSWORD\" -f drop \"$DRILL_DB\""
|
|||
|
|
echo "已删除临时库 $DRILL_DB"
|
|||
|
|
else
|
|||
|
|
echo "临时库 $DRILL_DB 保留(核对用);确认无误后重跑本脚本加 --drop-exec 删除。"
|
|||
|
|
fi
|
|||
|
|
exit 0
|
|||
|
|
else
|
|||
|
|
echo ""
|
|||
|
|
echo "结果:存在不一致,请检查备份文件与数据库结构。" >&2
|
|||
|
|
echo "临时库 $DRILL_DB 保留供排查。" >&2
|
|||
|
|
exit 1
|
|||
|
|
fi
|