mysql 8.0 不支持自动密码轮转,必须通过双密码机制(alter user ... retain current password + discard old password)实现无中断切换,否则直接 alter user 会立即失效旧密码,导致应用连接批量失败。

为什么不能直接改密码再重启应用
直接在 MySQL 里执行 ALTER USER 改完密码,就立刻让应用切换连接字符串——这是生产环境最常翻车的操作。应用会因认证失败批量报错 ERROR 1045 (28000): Access denied for user,甚至触发雪崩式超时熔断。
- MySQL 本身不提供密码自动轮换机制,
ALTER USER ... PASSWORD EXPIRE只能设单次过期时间,无法触发后续动作 - 应用层若硬编码密码,改完就得发版或热重载配置,中间必然存在“新旧密码并存窗口”或“连接中断窗口”
- 运维手动改密码 + 通知开发改配置 + 等待灰度发布 → 整个流程缺乏原子性和可观测性
用双密码机制实现无感切换
MySQL 8.0.19+ 原生支持双密码(primary 和 secondary),允许一个用户同时拥有两个有效密码,是唯一能规避业务中断的官方方案。
- 先设置 secondary 密码:
ALTER USER 'app_user'@'%' IDENTIFIED BY 'new_pass_202608' SECONDARY; - 此时原密码(primary)仍有效,所有现有连接不受影响
- 应用侧逐步切流:先更新一部分实例的连接串指向新密码,验证成功后再切剩余流量
- 确认全部迁移完成,再清除旧密码:
ALTER USER 'app_user'@'%' SECONDARY PASSWORD EXPIRE;
注意:SECONDARY 密码默认 30 天后自动过期,可通过 SET PERSIST validate_password.policy = MEDIUM; 调整策略,但不能关掉——否则 SECONDARY 也会被强度校验拦截。
绕不开的客户端兼容性陷阱
即使密码换成功了,如果应用用的是老 JDBC 驱动(比如 mysql-connector-java ERROR 2059 (HY000): Authentication plugin 'caching_sha2_password' cannot be loaded。
- 临时方案:改认证插件(仅限必须兼容旧客户端时)
ALTER USER 'app_user'@'%' IDENTIFIED WITH mysql_native_password BY 'new_pass_202608'; - 长期方案:升级驱动,并在连接串显式指定
?serverTimezone=UTC&allowPublicKeyRetrieval=true&useSSL=false - 千万别用
skip-grant-tables或直接 UPDATEmysql.user表——这些在 8.0 下会导致认证插件元数据损坏,后续ALTER USER会静默失败
密码文件与自动化脚本的安全边界
用 .my.cnf 存密码看似方便,但在 CI/CD 流水线或容器化部署中极易泄露。一旦被 git commit 或镜像层缓存,等于把数据库钥匙贴在代码仓库首页。
-
.my.cnf必须严格设权限:chmod 600 ~/.my.cnf,且不能出现在任何构建上下文路径中 - 推荐用环境变量传参:
mysql --defaults-extra-file=/run/secrets/mysql.cnf -e "SELECT 1",配合 Docker secrets 或 K8s Secret 挂载 - 轮换脚本里避免拼接明文密码:
echo "ALTER USER 'x'@'y' IDENTIFIED BY '${NEW_PASS}'" | mysql -S /var/run/mysqld.sock会被ps aux看见,应改用--defaults-file加载临时凭证文件
真正麻烦的不是改密码这个动作,而是确认“所有连接池、所有后台任务、所有离线脚本、所有监控探针”都已同步新凭据——漏掉任何一个,都会在半夜三点给你发告警。











