backup_database.sh 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196
  1. #!/bin/bash
  2. # MySQL 数据库备份脚本
  3. # 创建日期: 2025-12-05
  4. # 使用方法: ./backup_database.sh
  5. # 颜色定义
  6. RED='\033[0;31m'
  7. GREEN='\033[0;32m'
  8. YELLOW='\033[1;33m'
  9. NC='\033[0m' # No Color
  10. # 获取脚本所在目录
  11. SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
  12. PROJECT_DIR="$(dirname "$SCRIPT_DIR")"
  13. # 备份配置
  14. BACKUP_DIR="$SCRIPT_DIR"
  15. TIMESTAMP=$(date +"%Y-%m-%d_%H-%M-%S")
  16. BACKUP_FILE="backup_math_${TIMESTAMP}.sql"
  17. BACKUP_PATH="$BACKUP_DIR/$BACKUP_FILE"
  18. GZIP_BACKUP_PATH="${BACKUP_PATH}.gz"
  19. # 日志文件
  20. LOG_FILE="$BACKUP_DIR/backup.log"
  21. # 记录日志函数
  22. log() {
  23. local message=$1
  24. local color=${2:-$NC}
  25. echo -e "${color}[$(date '+%Y-%m-%d %H:%M:%S')] ${message}${NC}" | tee -a "$LOG_FILE"
  26. }
  27. # 检查是否在正确的目录
  28. if [ ! -f "$PROJECT_DIR/.env" ]; then
  29. log "错误: 未找到 .env 文件,请确保在正确的项目目录中执行" $RED
  30. exit 1
  31. fi
  32. # 开始备份
  33. log "=========================================" $GREEN
  34. log "开始 MySQL 数据库备份" $GREEN
  35. log "=========================================" $GREEN
  36. # 切换到项目目录
  37. cd "$PROJECT_DIR"
  38. # 创建 PHP 备份脚本
  39. PHP_SCRIPT="$BACKUP_DIR/backup_${TIMESTAMP}.php"
  40. cat > "$PHP_SCRIPT" << 'EOF'
  41. <?php
  42. require_once __DIR__ . '/../vendor/autoload.php';
  43. $app = require_once __DIR__ . '/../bootstrap/app.php';
  44. $kernel = $app->make(Illuminate\Contracts\Console\Kernel::class);
  45. $kernel->bootstrap();
  46. use Illuminate\Support\Facades\DB;
  47. try {
  48. // 创建备份内容
  49. $tables = DB::select('SHOW TABLES');
  50. $dump = "-- MySQL Database Backup\n";
  51. $dump .= "-- Generated on: " . date('Y-m-d H:i:s') . "\n";
  52. $dump .= "-- Database: math\n";
  53. $dump .= "-- FilamentAdmin Project Backup\n\n";
  54. $totalTables = count($tables);
  55. $currentTable = 0;
  56. foreach ($tables as $table) {
  57. $tableName = array_values((array)$table)[0];
  58. $currentTable++;
  59. echo "备份表 [$currentTable/$totalTables]: $tableName\n";
  60. // 获取建表语句
  61. $createTable = DB::select('SHOW CREATE TABLE ' . $tableName);
  62. if (isset($createTable[0])) {
  63. $dump .= "--\n-- Table structure for table `$tableName`\n--\n";
  64. $dump .= "DROP TABLE IF EXISTS `$tableName`;\n";
  65. $dump .= $createTable[0]->{'Create Table'} . ";\n\n";
  66. }
  67. // 获取表数据
  68. $dump .= "--\n-- Dumping data for table `$tableName`\n--\n";
  69. // 分批获取数据以避免内存问题
  70. $offset = 0;
  71. $limit = 1000;
  72. $hasData = false;
  73. do {
  74. $data = DB::table($tableName)->offset($offset)->limit($limit)->get();
  75. foreach ($data as $row) {
  76. $hasData = true;
  77. $values = [];
  78. $columns = [];
  79. foreach ((array)$row as $key => $value) {
  80. if (is_numeric($key)) continue; // 跳过数字索引
  81. $columns[] = "`$key`";
  82. if ($value === null) {
  83. $values[] = 'NULL';
  84. } elseif (is_string($value)) {
  85. // 处理特殊字符
  86. $value = str_replace(["\n", "\r", "\t"], ["\\n", "\\r", "\\t"], $value);
  87. $values[] = "'" . addslashes($value) . "'";
  88. } elseif (is_bool($value)) {
  89. $values[] = $value ? '1' : '0';
  90. } elseif (is_float($value) || is_double($value)) {
  91. $values[] = sprintf('%F', $value);
  92. } else {
  93. $values[] = $value;
  94. }
  95. }
  96. $dump .= "INSERT INTO `$tableName` (" . implode(', ', $columns) . ") VALUES (" . implode(', ', $values) . ");\n";
  97. }
  98. $offset += $limit;
  99. } while ($data->count() == $limit);
  100. if (!$hasData) {
  101. $dump .= "-- Table is empty\n";
  102. }
  103. $dump .= "\n";
  104. }
  105. // 添加备份结束标记
  106. $dump .= "--\n-- Backup completed successfully\n--\n";
  107. // 保存备份文件
  108. $filename = getenv('BACKUP_FILE') ?: 'backup.sql';
  109. file_put_contents($filename, $dump);
  110. echo "\n备份完成!\n";
  111. echo "备份文件: $filename\n";
  112. echo "文件大小: " . number_format(filesize($filename) / 1024, 2) . " KB\n";
  113. } catch (Exception $e) {
  114. echo "\n备份失败!\n";
  115. echo "错误信息: " . $e->getMessage() . "\n";
  116. exit(1);
  117. }
  118. EOF
  119. # 设置环境变量
  120. export BACKUP_FILE="$BACKUP_PATH"
  121. # 执行备份
  122. log "正在执行数据库备份..." $YELLOW
  123. if php "$PHP_SCRIPT" 2>&1; then
  124. # 备份成功
  125. log "数据库备份成功完成!" $GREEN
  126. # 压缩备份文件
  127. log "正在压缩备份文件..." $YELLOW
  128. if gzip -c "$BACKUP_PATH" > "$GZIP_BACKUP_PATH"; then
  129. log "压缩成功!" $GREEN
  130. log "压缩文件: $GZIP_BACKUP_PATH"
  131. log "原始文件: $BACKUP_PATH ($(ls -lh "$BACKUP_PATH" | awk '{print $5}'))"
  132. log "压缩文件: $(ls -lh "$GZIP_BACKUP_PATH" | awk '{print $5}')"
  133. else
  134. log "压缩失败!" $RED
  135. fi
  136. # 清理临时PHP文件
  137. rm -f "$PHP_SCRIPT"
  138. # 显示备份文件列表
  139. log "\n最近的备份文件:" $GREEN
  140. ls -lh "$BACKUP_DIR"/backup_math_*.sql.gz | tail -5 | awk '{print $9 " (" $5 ")"}'
  141. # 保留最近10个备份文件
  142. log "正在清理旧备份(保留最近10个)..." $YELLOW
  143. cd "$BACKUP_DIR"
  144. ls -t backup_math_*.sql.gz | tail -n +11 | xargs -r rm
  145. ls -t backup_math_*.sql | tail -n +11 | xargs -r rm
  146. log "清理完成!" $GREEN
  147. else
  148. # 备份失败
  149. log "数据库备份失败!" $RED
  150. rm -f "$PHP_SCRIPT"
  151. exit 1
  152. fi
  153. log "=========================================" $GREEN
  154. log "备份脚本执行完毕" $GREEN
  155. log "=========================================" $GREEN
  156. exit 0