MySQL 5.7.6+ 可通过 FAILED_LOGIN_ATTEMPTS 和 PASSWORD_LOCK_TIME 自动锁定账号,需用 ALTER USER 配置且仅对本地密码认证(mysql_native_password/caching_sha2_password)生效;account_locked 字段持久化,无需 FLUSH PRIVILEGES;LDAP/PAM 等外部认证下该机制无效。

MySQL 5.7+ 如何用 FAILED_LOGIN_ATTEMPTS 和 PASSWORD_LOCK_TIME 自动锁账号
MySQL 原生不支持“僵尸账号”这种模糊概念,但能通过账户密码策略自动锁定因暴力尝试或长期未用而失效的账号。关键在 CREATE USER 或 ALTER USER 时配置失败登录限制和锁定时长。
必须启用 validate_password 插件(仅影响密码强度)不是必须的,但 lock_option 功能依赖 MySQL 5.7.6+ 的原生账户锁定机制。
-
FAILED_LOGIN_ATTEMPTS设为 3~5 比较合理:设太小(如 1)容易误锁;设太大(如 10)削弱防护效果 -
PASSWORD_LOCK_TIME单位是天,不能写成小时或分钟;设为 0 表示永久锁定,需 DBA 手动ALTER USER ... ACCOUNT UNLOCK - 该策略只对使用密码认证的连接生效(即
authentication_string不为空、且plugin是mysql_native_password或caching_sha2_password) - 已锁定账号仍可被
DROP USER或RENAME USER,但无法登录,SELECT USER(), CURRENT_USER()会返回空或报错ERROR 3118 (HY000): Access denied for user ... (account is locked)
为什么 FLUSH PRIVILEGES 对账号锁定无效
账号锁定状态由 mysql.user 表中的 account_locked 字段控制,它是持久化字段,不是内存缓存。执行 ALTER USER 'u'@'h' ACCOUNT LOCK 或触发失败计数后,MySQL 会直接更新磁盘表并刷新内部状态,不需要手动刷新权限表。
-
FLUSH PRIVILEGES只重新加载权限相关字段(如Select_priv、Grant_priv),不影响account_locked - 误执行
FLUSH PRIVILEGES后发现账号没锁住?大概率是ALTER USER语句本身没成功(比如用户不存在、语法拼错、权限不足),而不是缓存没刷 - 验证是否真锁定:用该账号连一次,看错误是不是
ERROR 3118;或者查SELECT user, host, account_locked FROM mysql.user WHERE user = 'u'
如何批量清理长期未登录的“僵尸账号”
MySQL 不记录账号最后登录时间,mysql.user 表里没有 last_login 字段。所谓“长期未用”只能靠外部日志或变通方式推断。
- 开启通用查询日志(
general_log = ON)并定期解析,成本高、影响性能,生产环境慎用 - 更可行的是结合操作系统审计 + 连接池日志:比如应用用
myuser@10.0.1.%连接,查 ProxySQL 或 MaxScale 的访问日志,连续 90 天无记录就标记为待清理 - 执行清理前务必确认:该账号没被定时任务、备份脚本、监控探针等静默调用;
DROP USER不可回滚,建议先RENAME USER改名加_zombie_2024后缀观察一周 - 注意:MySQL 8.0.14+ 支持
SET PERSIST lock_wait_timeout = 30类似功能,但依然不提供登录时间戳,别被文档误导
MySQL 账号锁定和操作系统的 PAM/LDAP 锁定不是一回事
如果 MySQL 配置了外部认证(如 authentication_ldap_sasl 或 PAM),账号锁定行为完全由外部服务决定,MySQL 本地的 ACCOUNT LOCK 和 FAILED_LOGIN_ATTEMPTS 不起作用。
- 例如 LDAP 用户输错密码,锁的是 LDAP 目录里的账号,MySQL 只是转发认证请求;此时查
mysql.user.account_locked仍是N - 同样,用
ALTER USER ... ACCOUNT LOCK锁住一个 LDAP 用户,实际登录时可能仍成功——因为 MySQL 把认证交给了 LDAP,自己没校验密码 - 判断依据看
SELECT plugin FROM mysql.user WHERE user = 'u':如果是authentication_ldap_sasl、auth_pam等,本地锁定策略全部失效
FAILED_LOGIN_ATTEMPTS 就以为万事大吉,结果发现用的是 LDAP 认证,根本没走 MySQL 的失败计数逻辑。










