使用 gcloud sql instances restart 时,如何确保连接池在零停机窗口内完成重连?
解读
在国内互联网面试场景里,这道题考察的是“可控重启”而非“无脑重启”。
面试官真正想听的是:
- 你能否把 Cloud SQL 提供的原生高可用能力(HA 实例、故障转移)与 应用侧连接池行为 结合起来,做到“重启对业务透明”;
- 你能否给出 可落地的代码/脚本/配置片段,让 DBA 和研发都能按图施工;
- 你能否在 30 秒~2 分钟 的国内可接受中断 SLA 内,把 连接池重连 与 事务重试 全部闭环。
一句话:不是“怎么重启”,而是“怎么让重启看起来像没重启”。
知识点
- HA 实例架构:主实例与热备实例分处不同可用区,通过内部健康检查实现 <30 s 自动故障转移,重启命令默认先切到备节点再重启原主节点。
- 重启类型:
gcloud sql instances restart默认走 切换式重启(先 failover 再重启旧主),- 加
--no-failover则为 就地重启(直接断连),国内生产环境禁止直接使用。
- 连接池四大参数:
- initial-size / min-idle:保持预热连接,
- max-lifetime:单条连接最大存活时间,建议 ≤ 5 min 防止 MySQL 的
wait_timeout踢连接, - idle-timeout:空闲回收阈值,建议 30 s,
- connection-test-query 或 connection-timeout:探活语句
SELECT 1,验证间隔 ≤ 5 s。
- 探活策略:
- testOnBorrow=false + testWhileIdle=true(HikariCP 默认)即可在后台异步探活,
- Google 推荐配合 Cloud SQL Auth Proxy 的
-max_connections与-refresh_config_interval=30s参数,让代理层先于池发现 IP 漂移。
- 事务重试:
- 对 MySQL 引擎需捕获
SQLState=08xxx与错误码2013/2006, - 对 PostgreSQL 需捕获
SQLState=57P01/57P02/08003, - 采用 ** exponential backoff + jitter**,最多 3 次,总耗时 < 15 s。
- 对 MySQL 引擎需捕获
- 国内网络合规:
- 若走 Private IP,需确保 VPC 对等或 Private Service Connect 已打通,
- 若走 Public IP 必须加 SSL=required 且 IP 白名单只放 NAT 出口,防止重启后新 IP 未同步白名单导致池永久拒绝。
- 观测指标:
- stackdriver metric:
cloudsql.googleapis.com/database/mysql/connections/failed突增即代表池未重连成功, - 应用侧埋点:连接池
getConnection()耗时 P99 超过 1 s 即触发告警。
- stackdriver metric:
答案
步骤一:强制使用 HA 实例并执行切换式重启
gcloud sql instances restart prod-mysql \
--project=my-gcp-project \
--async # 立即返回,脚本可继续往下做健康检查
关键点:绝不加 --no-failover,让 Google 先完成 <30 s 的跨区切换。
步骤二:连接池配置(以 Spring Boot 2.x + HikariCP 为例)
spring:
datasource:
type: com.zaxxer.hikari.HikariDataSource
hikari:
minimum-idle: 5
maximum-pool-size: 20
max-lifetime: 300000 # 5 min,防止重启后旧连接仍存活
idle-timeout: 30000
connection-test-query: SELECT 1
validation-timeout: 5000
leak-detection-threshold: 10000
解释:max-lifetime < 云端 wait_timeout,保证重启后池会主动淘汰可能已失效的连接;validation-timeout=5 s 让探活失败快速抛错,触发池重建。
步骤三:事务层自动重试(基于 Spring Retry)
@Retryable(value = {SQLException.class, QueryTimeoutException.class},
maxAttempts = 3, backoff = @Backoff(delay = 200, multiplier = 2, random = true))
public void bizOperation() {
// 正常业务 SQL
}
注意:只重试 读事务或 幂等写;非幂等写需业务层去重或采用 PTC(Per-Transaction UUID) 防重框架。
步骤四:重启流程编排(国内常用 GitLab CI + Ansible)
- pre-restart:通过 MHA Manager 或 自研脚本 把读写流量 禁写 3 s,等待活跃事务 ≤ 5;
- restart:执行上述
gcloud命令; - post-restart:轮询 Cloud SQL Admin API
https://sqladmin.googleapis.com/v1/projects/{proj}/instances/{id}/connectSettings直到 ipAddress 字段变更且 state=RUNNABLE; - traffic back:Ansible 批量 reload Envoy/SLB,让新连接落到新 IP;
- 观测:Grafana 看 jetty_connections_active 与 cloudsql_failed_connections,2 min 内回归基线即判定成功。
结果:
- 业务侧感知到的中断 ≈ TCP 重传超时 1~3 s;
- 连接池重连在 30 s 内全部完成;
- 零人工干预,符合国内 SRE 1-5-10 要求(1 min 发现、5 min 定位、10 min 恢复)。
拓展思考
-
如果实例不是 HA,而是单节点开发库,能否做到“零停机”?
答:不能。单节点重启必然 30~90 s 不可用,此时只能把连接池 max-lifetime 降到 60 s 并开启 熔断器(如 Sentinel),让业务快速失败并返回 “系统维护中”,重启完再自动闭合熔断,把“停机”转成“降级”。 -
国内双活场景下,跨地域只读实例如何同步重连?
采用 Cloud SQL 跨区域只读副本 时,重启主库会触发 VIP 漂移到只读副本。连接池需配置 读写分离插件(ShardingSphere / MyCat),读库列表动态刷新间隔 ≤ 10 s,否则重启后新只读 IP 未同步,读流量依旧打到旧 IP 导致 4040 错误。 -
Terraform 基础设施即代码如何固化上述流程?
用 null_resource + local-exec 调用 gcloud 命令,并把 restart 后校验脚本 写成 Terraform provisioner,让 GitLab Pipeline 只跑terraform apply就能完成 “代码即重启”,杜绝人工黑屏操作,满足国内金融云 “变更可审计” 合规要求。