杰奇CMS生产环境全链路备份策略实战指南——从数据库到模板的容灾方案
“昨晚凌晨三点,用户反馈整站无法访问,登录服务器发现chapter表已经膨胀到2.3GB,mysqldump卡死在半路,数据修复花了整整六小时。”这是去年接手一个日活3万的杰奇小说站时遇到的真实场景,今天以这个案例为起点,分享一套结合性能优化与灾难恢复的完整备份体系。
数据库备份:层级解耦与分表预处理
场景:章节表过大导致备份失败
当单表超过1GB时,传统mysqldump会锁表并消耗大量IO,你需要做两件事:
按主键范围分批导出
每批导出50万条记录
start=$((i * 500000))
end=$(((i+1) * 500000))
mysqldump -u root -p yourdb chapters --where="id BETWEEN $start AND $end" > chapter_part_${i}.sql &
done
wait
强制启用分表策略
在config.php中添加分表规则(以100万条为阈值):
define('CHAPTER_TABLE_SPLIT', 1000000);
// 在model层检测表数据量
function getChapterTable($bookId) {
$count = M('chapters')->where("book_id={$bookId}")->count();
$tableIndex = ceil($count / CHAPTER_TABLE_SPLIT);
return "chapter_{$tableIndex}";
}
缓存配置:让备份不再影响读写性能
场景:备份期间网站访问速度骤降
因为备份仍会产生临时读锁,解决方案是读写分离+缓存二级保护:
Redis缓存章节热点数据
在chapter_read.html模板前加入:
$cacheKey = "chapter_{$chapterId}_content";
$content = S($cacheKey); // 使用ThinkPHP缓存
if(!$content) {
$content = M('chapter')->where("id={$chapterId}")->getField('content');
S($cacheKey, $content, 3600); // 缓存1小时
}
数据库备份时切换只读实例
在备份脚本开头执行:
-- 临时将主库设为只读 FLUSH TABLES WITH READ LOCK; -- 备份完成后解锁 UNLOCK TABLES;
模板标签与移动端:备份内容一致性保障
场景:修改模板导致备份文件与数据库不同步
很多二次开发者直接修改/templates目录下的文件,但备份时漏掉模板会导致恢复后样式错乱。
推荐做法:
将模板自定义函数分离到/extend/taglib.php:
// 自定义分页标签
function custom_paging($total, $perpage) {
$str = '<div class="pagination">';
for($i=1; $i<=ceil($total/$perpage); $i++) {
$str .= "<a href='?page={$i}'>{$i}</a>";
}
return $str . '</div>';
}
// 模板中调用:{custom_paging total="1000" perpage="20"}
移动端适配时务必备份:
在/mobile/template/下创建独立目录,并通过User-Agent判断:
if(preg_match('/Mobile|Android|iPhone/i', $_SERVER['HTTP_USER_AGENT'])) {
define('TEMPLATE_PATH', './mobile/template/');
} else {
define('TEMPLATE_PATH', './template/');
}
备份脚本需同时压缩template和mobile/template目录。
阅读页卡顿:索引与归档联合优化
场景:最新章节频繁写入导致索引碎片
阅读页的book_id + chapter_order联合索引如果被频繁插入,应定期重建:
-- 每周执行一次重建索引 ALTER TABLE chapters ENGINE=InnoDB; OPTIMIZE TABLE chapters;
归档机制(年更老书分离):
创建全年数据备份表chapters_2023,通过事件触发:
CREATE EVENT archive_old_chapters ON SCHEDULE EVERY 1 MONTH DO INSERT INTO chapters_2023 SELECT * FROM chapters WHERE create_time < DATE_SUB(NOW(), INTERVAL 2 YEAR); DELETE FROM chapters WHERE create_time < DATE_SUB(NOW(), INTERVAL 2 YEAR);
定期维护:自动清理与监控报警
场景:忘记清理日志导致备份文件过大
在crontab中添加每日任务(每天凌晨3点执行):
0 3 * * * /usr/bin/php /path/to/cleanup.php
cleanup.php脚本内容:
// 清理超过7天的错误日志
$logFile = './Runtime/Logs/';
$files = glob($logFile . '*.log');
foreach($files as $f) {
if(filemtime($f) < strtotime('-7 days')) {
unlink($f);
}
}
// 清理碎片章节(已删除书籍的残留数据)
M()->query("DELETE c FROM chapters c LEFT JOIN books b ON c.book_id=b.id WHERE b.id IS NULL");
健康检查脚本(备份前执行):
$tableSize = M()->query("SELECT ROUND(SUM(data_length+index_length)/1024/1024,2) AS size FROM information_schema.tables WHERE table_schema='yourdb'");
if($tableSize[0]['size'] > 5000) { // 超过5GB报警
mail('admin@yourdomain.com', '数据库空间预警', '当前容量:'.$tableSize[0]['size'].'MB');
}
灾难恢复演练:模拟实战验证
每月进行一次恢复测试:
- 新建测试数据库
yourdb_bak - 执行还原命令:
mysql -u root -p yourdb_bak < full_backup.sql - 对比关键数据量:
SELECT count(*) FROM chapters yourdb vs yourdb_bak - 检查模板路径一致性:
diff -r /template /restore_template
关键指标监控:
- 备份文件小于数据库实际容量的50% → 检查是否漏掉二进制日志
- 恢复时间超过备份时间3倍 → 需要优化插入语句分批执行
所有的备份策略最终都要回归到“恢复验证”这个环节,上个月刚帮一个同行恢复了一个丢失6章数据的站,正是靠提前准备的chapter_part_*.sql分批文件,在15分钟内完成了数据补全,备份的最高境界不是存储多份数据,而是确保每一份数据都能在10分钟内恢复上线。



发表评论