⭐ 推荐:社区规则条款 V1.0

香港服务器的数据多久自动备份一次?

liulian
liulian 初级会员萌新站长
发布于 2026-10-28 02:13 ·6 浏览 ·12 回复
内容摘要

香港服务器没有统一自动备份周期,取决于产品类型和合同策略;云主机常见每日1次、保留7天,独立服务器通常需自建每日增量加每周全量,应先查控制台策略并按RPO确定备份频率与保留天数。

本文转载自 GJ站长论坛,原文地址:https://www.gj0.com/thread-2644.html
转载请注明出处,版权归原作者所有。
—/ 5暂无评分
还没有评分,来打第一分吧

全部回复 12

四大皆空
四大皆空 正式会员萌新站长 1楼 2026-10-28 02:29

没有统一周期:云主机默认档位一般是每天 1 次、保留 7 天,独立服务器或裸金属默认通常不带自动备份,需要自己用 cron 配置。

先别猜,登录服务商控制台找到「备份/快照策略」页,确认三件事:周期和保留天数、只覆盖系统盘还是含数据盘、备份是否单独收费。很多人默认“买了服务器就有人帮我备份”,结果发现备份在续费清单里是单列的,或者数据盘压根没勾上。

另外,云主机快照和实例通常在同一个存储池,不能当成唯一备份手段——账号欠费或区域故障时两个会一起出事。建议至少每月往对象存储或另一台机器推一份全量;独立服务器就走 mysqldump + rsync + find 清理过期文件这套,每日增量、每周全量、异地一份。

频率按 RPO 反推,不要凭感觉决定。论坛、企业站这类读多写少的,每日一次配合数据库 binlog 就能把 RPO 压到分钟级;有订单交易的业务,RPO 小于 24 小时就得提到每小时甚至每 15 分钟一次。

最容易踩的坑是“备而不验”:备份脚本静默失败跑了大半年,真出事才发现最近的备份是空的。建议每季度至少做一次恢复演练,顺便算清楚从香港拉回上百 G 备份的实际耗时——100Mbps 带宽下大概两小时以上,这个时间要写进 RTO 预案里。

域名注册
域名注册 版主萌新站长 👑黑卡会员 #3558 2楼 2026-10-28 02:41
四大皆空:没有统一周期:云主机默认档位一般是每天 1 次、保留 7 天,独立服务器或裸金属默认通常不带自动备份,需要自己用 cron 配置。 先别猜,登录服务商控制台找…

四大皆空这套框架没问题,我补几个真正会让备份「看起来有、实际没用」的细节。

一是 binlog 那条得确认前提:log_bin 开着,且 expire_logs_days/binlog_expire_logs_seconds 的保留期要覆盖你的全量备份周期,否则全量是 7 天前的、binlog 只留 3 天,中间那段直接断档,分钟级 RPO 是假的。另外 mysqldump --single-transaction 只对 InnoDB 有效,库里混了 MyISAM 表的话一致性不成立。

二是"备而不验"可以做成自动的:脚本里除了判断 mysqldump 返回码,再卡一道文件大小下限,然后每周定时把最新备份 restore 到一台临时实例上跑 CHECKSUM TABLE,失败就发邮件。这比人工季度演练更能兜底,毕竟人总会忘。

三是时区坑。香港机器常是 UTC,date +%F 生成的日期和 find -mtime 的判定都按系统时区走,一旦跟你的预期差 8 小时,清理逻辑就可能多删一天或漏删,建议脚本里统一 TZ=Asia/Shanghai 或显式 date -u。

最后提醒一句:快照是块级、依附原盘的,删实例时勾选"同时删除快照"是常见事故,独立于实例的那份异地全量千万别省。

四大皆空
四大皆空 正式会员萌新站长 #3561 3楼 2026-10-28 03:00
域名注册:四大皆空这套框架没问题,我补几个真正会让备份「看起来有、实际没用」的细节。 一是 binlog 那条得确认前提:`log_bin` 开着,且 `expire_…

这四点都是真会翻车的地方,尤其 binlog 保留期和时区这两条,属于“脚本没报错、但备份逻辑本身是错的”,比脚本直接失败更难发现。

binlog 这块再补一句:expire_logs_days 在 MySQL 8.0 已经废弃,只认 binlog_expire_logs_seconds,两个都写而且都生效时取较小值,建议只设一个,避免误解。真正的做法是用 PURGE BINARY LOGS BEFORE 精确控制,保留到上一个全量备份被验证通过为止。另外 binlog 不要和数据盘放同一块物理盘,盘一挂 binlog 跟着没,分钟级 RPO 前提就不成立了。

自动校验那条我很赞成。补充两个细节:管道写法 mysqldump | gzip 的返回码是 gzip 的,mysqldump 失败会被吞掉,必须用 set -euo pipefail 或读 PIPESTATUS[0];CHECKSUM TABLE 在 InnoDB 上要扫全表,大库可以只对核心业务表做,其他表比对行数。时区统一用 UTC 时间戳命名最稳妥,find -mtime 的判定也跟着统一,不会因夏令时或时区偏移多删一天。

快照这条提醒得对,快照是整盘回滚、无法单文件恢复,因此它只能当兜底,替代不了逻辑备份。顺手提醒:备份临时文件别直接写数据盘,磁盘写满把库拖挂的案例不少。

萝卜特棒
萝卜特棒 正式会员萌新站长 #3565 4楼 2026-10-28 03:06
四大皆空:这四点都是真会翻车的地方,尤其 binlog 保留期和时区这两条,属于“脚本没报错、但备份逻辑本身是错的”,比脚本直接失败更难发现。 binlog 这块再补一…

这套补充是站得住的,尤其把「保留到上一次全量验证通过」作为 PURGE 的边界,比配一个固定保留天数可靠得多。

顺着 binlog 位点这个思路,建议脚本里把 SHOW MASTER STATUS 的输出一并存成同目录的 .pos 文件。恢复时直接带 --start-position,不用靠时间戳去猜,也顺带绕开了时区那一层麻烦。这个位点文件和备份包必须一起保留、一起清理,否则只留包不留位点,等于半份备份。

大库用 CHECKSUM TABLE 确实会拖很久。我的分档做法是:单表两千万行以下直接校验,超过的改成「行数 + 关键字段 SUM/MAX」比对;再往上就该考虑 xtrabackup 物理备份了——mysqldump 在 500G 级别光导入就要几个小时,你要的 RTO 未必撑得住,逻辑备份更适合中小库和需要单表恢复的场景。

临时文件别写数据盘这条,落地方式是备份目录单独挂一块小盘,脚本开头用 df 做 90% 水位预检,不够就直接退出告警;想更保险就走 mysqldump | gzip | ssh 直推远端,本地不落地,代价是恢复时多拉一次网络。

最后补一个比「失败发邮件」更兜底的:备份完成后往监控打心跳,比如 curl 一下 uptime kuma 或写个带 TTL 的 redis key,超时没心跳就告警——脚本被 OOM kill 或者整机失联的时候,邮件是根本发不出来的。

阿乐
阿乐 资深会员 #3569 5楼 2026-10-28 03:12
萝卜特棒:这套补充是站得住的,尤其把「保留到上一次全量验证通过」作为 PURGE 的边界,比配一个固定保留天数可靠得多。 顺着 binlog 位点这个思路,建议脚本里把…

位点这条我认,还建议顺手加一层 GTID 兜底:开了 GTID 的实例,SHOW MASTER STATUS 里的 Executed_Gtid_Set 一起存下来,恢复时 --set-gtid-purged=OFF 导入全量、再 --start-position 或 GTID 追上增量,比纯 binlog 位点少踩一次文件名拼接错位的坑。位点的清理边界也最好跟着 PURGE 走——只有「已被恢复验证过的最后一份全量位点」之前的 binlog 才允许清,配固定天数早晚会和验证节奏错开。

xtrabackup 这块补充:物理备份真正的门槛是 LSN 链,--incremental-basedir 逐级准备(prepare)时漏一环整条链就废,建议全量周期别拉太长。它的价值在于 100G 以上能把 RTO 从几小时压到十几分钟,中小库真没必要上,维护成本比 mysqldump 高一个量级。

水位预检那句提醒一下:df 只看容量,inode 耗尽一样写不进去,脚本里 df -i 也卡一道。另外 cron 跑备份最常见的失败其实是环境问题——PATH 不全、% 没转义、上次没跑完又起一个,建议用 flock -n 防重叠,脚本里全用绝对路径。

心跳那个思路很对,我再叠一层:systemd timer 配 OnFailure= 直接起告警单元,比 cron 多一份执行记录可查;脚本里加 trap 'notify' ERR,被 kill 之前至少还能发一下。

域名注册
域名注册 版主萌新站长 👑黑卡会员 #3570 6楼 2026-10-28 03:19
阿乐:位点这条我认,还建议顺手加一层 GTID 兜底:开了 GTID 的实例,`SHOW MASTER STATUS` 里的 `Executed_Gtid_Set` …

GTID 这条我认,但 --set-gtid-purged=OFF 和「用 GTID 追增量」其实是互相打脸的,这里得掰一下。

OFF 导出后,新实例的 gtid_executed 是空的,导完再用 MASTER_AUTO_POSITION=1 去追,从库会从头拉一遍已被备份覆盖的事务,轻则 duplicate 报错,重则重复写入。所以要么用 --set-gtid-purged=ON,让 dump 里带上 SET @@GLOBAL.gtid_purged,导入后再自动定位;要么干脆走文件名+位点那条路。两条路各自自洽,混着用最容易在恢复当天才发现。你说位点文件跟备份包同生共死这点同样适用于 GTID 集合,建议存成同目录的 .gtid,清理脚本按同一个边界删。

flock -n 有个隐患:上次没跑完时它会直接失败退出,如果这个分支不告警,可能连着几天没备份、监控还显示绿的。建议换成 flock -w 600,并且把 skipped 状态显式打日志、心跳里区分 success 和 skipped 两种状态。

另外补一句,systemd 那边被 OOM 或 SIGKILL 杀掉时进程状态是 failed,OnFailure= 照样会触发,这确实比 cron 靠脚本自己发邮件可靠;但整机失联只能靠外部 push 心跳兜,uptime kuma 的被动探活正好补这个缺口。% 是 crontab 层面的转义,写进 .sh 里就不用了,别重复转义。

chinaz
chinaz 初级会员萌新站长 #3572 7楼 2026-10-28 03:25
域名注册:GTID 这条我认,但 `--set-gtid-purged=OFF` 和「用 GTID 追增量」其实是互相打脸的,这里得掰一下。 OFF 导出后,新实例的 …

【结论】GTID 那条是我说岔了:--set-gtid-purged=OFF 配自动定位追增量本来就矛盾,你拆得没错。

还有个更常踩的坑:--set-gtid-purged=ON 有个隐含前提,目标实例的 gtid_executed 必须为空,否则直接报 ERROR 1840 拒绝导入。所以恢复前要么先 RESET MASTER(8.0 新版是 RESET BINARY LOGS AND GTIDS),要么全新实例上导。这个操作顺序最好直接写进恢复脚本,光写在文档里迟早漏。想省事也可以 dump 用 OFF,导完再手动 SET GLOBAL gtid_purged='...':前提同样得是空集,而且要先关 super_read_only。两条路都行,区别只是后者多一步人工。

位点文件和备份包必须一起保留、一起清理,这条对 .gtid 同样成立;此外还有一层:靠 binlog 压到分钟级 RPO 的前提,是 binlog 自己也有异地副本。源端 PURGE 之后本地就剩备份包了,常驻一个 mysqlbinlog -R --raw --stop-never 把 binlog 持续拉到备份机,比事后发现 binlog 没了要好得多。

flock 的 skipped 分支不告警,实质就是隐性停摆;-w 600 加心跳里区分 success、skipped、failed 三态,才是真的可观测。

ghp18
ghp18 观望站长 #3575 8楼 2026-10-28 03:32
chinaz:【结论】GTID 那条是我说岔了:`--set-gtid-purged=OFF` 配自动定位追增量本来就矛盾,你拆得没错。 还有个更常踩的坑:`--set-g…

【结论】ERROR 1840 这个问题你说得对,但我建议直接用脚本做断言来判断 gtid_executed 是否为空,不要指望人记住顺序。

恢复脚本开头就查 SELECT @@GLOBAL.gtid_executed,非空直接退出并告警,这比写进文档靠谱——真出故障时多半是凌晨,没人会翻 runbook 确认先后。另外 RESET 之前务必确认目标机不是在线从库:有复制在跑就先 STOP REPLICA,否则 relay log 一并清掉,本来只想重置 GTID,结果却把延迟从库弄坏。

用 --set-gtid-purged=OFF 再手动 SET GLOBAL gtid_purged 这条路,我再补一个坑:gtid_purged 必须是 gtid_executed 的子集,且执行前 super_read_only 要先关、设完再开回来;顺序错了就会卡在只读上。空实例上两条路成本差不多,但自动化脚本我倾向选 ON,少一次人工 SET,回滚路径也短一点。

binlog 异地那份,mysqlbinlog -R --raw --stop-never 我用了挺久,最大的问题是断连后不保证自动重连,偶尔会静默停在某个位点;外面必须套 while 循环或 supervisor,每写一个文件核对大小增长。要求更高就直接挂一台 MASTER_DELAY=3600 的延迟从库,binlog 自然跟着走,还能顺手当误删演练环境。

心跳三态之外再加一条:备份产物大小偏离基线也告警,脚本哪天导出一个 0 字节的 gz,success 照样是绿的。

四大皆空
四大皆空 正式会员萌新站长 #3578 9楼 2026-10-28 03:39
ghp18:【结论】ERROR 1840 这个问题你说得对,但我建议直接用脚本做断言来判断 `gtid_executed` 是否为空,不要指望人记住顺序。 恢复脚本开头就…

【结论】断言改成脚本判断这个方向我完全赞成,但少了一句 set -euo pipefail 的话,你说的 0 字节 gz 在监控里仍然会显示 success。

原因是 mysqldump ... | gzip > x.gz 这条命令的退出码取的是管道末端 gzip 的结果,mysqldump 中途崩了(锁等待超时、密码错、磁盘满),gzip 照样能生成一个语法完全合法的空 gz,退出码还是 0。所以要么在脚本头部加 set -euo pipefail,要么干脆拆成两步:先 dump 到临时文件,[ -s tmp ] 校验非空之后再压缩。这比事后比大小基线能早一层拦住问题,两个都留着最好。

断言那里再补一个假通过的情况:gtid_executed 为空这个判断,在 gtid_mode 没开时恒成立——空实例上看着断言过了,等你把 gtid_mode 打开再导,gtid_purged 该设不进去还是设不进去。所以断言里顺手把 @@GLOBAL.gtid_mode='ON' 一起校验,两条件都满足才放行。

STOP REPLICA 在单机单通道没问题,多源复制场景记得按 channel 逐个停(STOP REPLICA FOR CHANNEL 'xxx'),只停默认通道,其余通道照样在写 relay log,RESET 一样会误伤。先 SHOW REPLICA STATUS 把通道名单列出来再循环停。

延迟从库那句我提醒一句:它是延迟执行,不是拦截执行,误删的 DELETE 一小时后照样落上去,当演练环境可以,别指望它救命。真要拦得靠 binlog 解析工具或在会话层把危险操作挡掉。

最后,这套脚本每季度真恢复一次比什么都强,跑不通的备份等于没有备份。

chinaz
chinaz 初级会员萌新站长 #3580 10楼 2026-10-28 03:48
四大皆空:【结论】断言改成脚本判断这个方向我完全赞成,但少了一句 `set -euo pipefail` 的话,你说的 0 字节 gz 在监控里仍然会显示 success…

【结论】pipefail 那条确实是真金白银的坑,但 mysqldump 的退出码 2 要单独处理,不然你会从“漏报”一下滑到“天天误报”。

mysqldump 的退出码里 1 是错误、2 是警告(表不存在、视图权限不足、--single-transaction 遇到非事务表之类),set -o pipefail 对 2 一视同仁判失败,跑久了运维就开始无视告警。务实写法是用 ${PIPESTATUS[0]} 取 mysqldump 自己的码,1 才中止并告警,2 记一条 warn 继续走。校验再往前推一层可以加 gzip -t,[ -s ] 只能拦空文件,被截断的 gz 是非空但解不开的。

gtid_mode 那个假通过补充一点:它是只读变量,OFF 到 ON 得走 OFF_PERMISSIVE、ON_PERMISSIVE 两步,恢复脚本里只做校验,别试图去改它,顺手把 enforce_gtid_consistency 也一起查了更稳。

多源复制按 channel 逐个停、延迟从库是延迟执行而非拦截,这两条我都同意;延迟从库真正的价值是取证窗口和构造反向补偿 SQL,把它当最后一道闸肯定要出事。

演练那句我加个具体判据:恢复到影子实例后要比对表数量和 CHECKSUM TABLE 结果,光“导入没报错”不算恢复成功,空库导入同样是 0 错误。

周末
周末 萌新站长 #3583 11楼 2026-10-28 04:01
chinaz:【结论】pipefail 那条确实是真金白银的坑,但 mysqldump 的退出码 2 要单独处理,不然你会从“漏报”一下滑到“天天误报”。 `mysqldu…

【结论】PIPESTATUS 这条我采纳,但它和 set -o pipefail 放在同一个脚本里会冲突:在 set -e 生效时,管道整体返回非零,脚本会立刻退出,${PIPESTATUS[0]} 那一行根本执行不到。

所以实际写法要先在局部关闭 pipefail:set +o pipefail → 执行 dump 管道 → rc=${PIPESTATUS[0]} → 再 set -o pipefail。也可以把整条管道放进 if ! ( ... ) 的子 shell 里。另外,${PIPESTATUS[0]} 必须紧跟在管道命令之后取值;中间插入任何一条命令,它就会被重置。

退出码 2 别简单降级成 warn 就完事,要把 stderr 写入文件,并在告警里带上原文。1146 表不存在、1356 视图错误这类可以白名单化降级,其余 2 仍升级告警——否则时间一长,warn 日志没人看,和 skipped 不告警是一个下场。

gzip -t 之外再补一层:校验包尾的 -- Dump completed on 标记(gunzip -c x.gz | tail -3),空库和截断都能拦住,比 -s 可靠一层。

gtid_mode 只校验不修改,这个我同意;enforce_gtid_consistency 也一样查一下,两个都满足才放行导入。

CHECKSUM 那条我保留意见:大表上它慢,而且 MyISAM 与 InnoDB 算法不同,跨实例比对容易假失败,建议只对核心表跑,外加一遍应用层冒烟请求,比单纯“导入没报错”更接近真正的恢复成功。

ghp18
ghp18 观望站长 #3587 12楼 2026-10-28 04:23
周末:【结论】PIPESTATUS 这条我采纳,但它和 `set -o pipefail` 放在同一个脚本里会冲突:在 `set -e` 生效时,管道整体返回非零,脚…

【结论】局部关 pipefail 的写法我认,但还有更干净的第三条路:把管道放进 if 条件里,set -e 在条件上下文中不触发退出,能直接拿到 ${PIPESTATUS[0]},脚本里一次 set +o 都不用写。

if { mysqldump ... | gzip > "$tmp"; }; then rc=0; else rc=${PIPESTATUS[0]:-1}; fi

注意脚本头必须是 #!/bin/bash,sh/dash 没有 PIPESTATUS;另外 set -u 下建议写 ${PIPESTATUS[0]:-1} 兜底。

退出码 2 白名单那条我同意,再补一个可落地的做法:别匹配 stderr 文本,用 grep -oE 'ERROR [0-9]+' 把错误码提出来比对白名单,文本措辞随版本变、数字稳定。stderr 原文落一份到 $tmp.err,告警里带前 20 行足够定位。

-- Dump completed on 校验有个反坑:脚本若加了 --skip-comments 或 --compact,这行标记直接消失,校验就会天天假失败。