mysql 5.7+ 中识别长期未登录账号只能依赖 password_last_changed 字段或 general_log/audit log 日志;performance_schema.users 和 information_schema.processlist 等元数据表无法准确反映登录时间,误用将导致严重误判。

MySQL 5.7+ 中如何识别长期未登录的账号
MySQL 本身不直接记录用户“最后登录时间”,last_wait_time 是 Performance Schema 中 events_waits_summary_by_user_by_event_name 表的聚合字段,反映的是某用户最近一次等待事件的耗时,**完全不能代表登录时间或活跃状态**。靠它判断僵尸账号会严重误判——比如一个只执行定时任务的账号,可能 last_wait_time 很新,但实际已弃用;而一个被禁用但从未触发等待事件的账号,该值甚至为 NULL。
真正可用的依据只有两个:
-
mysql.user表中的password_last_changed(MySQL 5.7.6+),可间接反映账号是否被主动维护过 - 开启并解析 general_log 或 audit log(如 MySQL Enterprise Audit 或 Percona Audit Log Plugin),这是唯一能拿到真实登录时间的方式
启用 general_log 并提取登录行为的最小可行方案
如果没开审计日志,general_log 是最快落地的选择(仅限开发/测试环境,生产慎用):
SET GLOBAL general_log = ON; SET GLOBAL log_output = 'table'; -- 写入 mysql.general_log 表,比文件更易查
之后等 1–2 周,再运行:
SELECT DISTINCT user_host FROM mysql.general_log WHERE command_type = 'Connect' AND event_time > DATE_SUB(NOW(), INTERVAL 90 DAY);
注意点:
-
user_host格式是'user@host',需用SUBSTRING_INDEX(user_host, '@', 1)提取用户名 - 该表无索引,数据量大时查询慢,建议定期导出后清理:
TRUNCATE TABLE mysql.general_log - MySQL 8.0+ 的
performance_schema.users表有CONNECTIONS_ABORTED和CONNECTIONS_RECEIVED,但仍是累计值,无法反推最后一次登录时间
安全清理前必须验证的三件事
别急着 DROP USER。先确认:
- 该账号是否在应用配置、中间件连接池、定时脚本或备份命令中硬编码(搜代码库 +
grep -r 'username' /etc/ /var/www/) - 是否被其他账号通过
PROXY权限代理使用:SELECT * FROM mysql.proxies_priv WHERE host='%' AND user='target_user' - 是否拥有数据库级权限却未被显式授权到任何 DB:
SELECT * FROM mysql.db WHERE user='target_user'—— 这类账号可能通过GRANT ... ON *.*获权,容易遗漏
清理时优先用 REVOKE ALL PRIVILEGES ON *.* FROM 'user'@'host' + ALTER USER 'user'@'host' ACCOUNT LOCK(MySQL 5.7.6+),观察 1 周无报错再 DROP USER。
为什么不要依赖 information_schema.PROCESSLIST 判断活跃性
SELECT * FROM information_schema.PROCESSLIST 只显示当前连接,快照式数据。一个每天凌晨跑 10 秒 SQL 的账号,在你查 PROCESSLIST 的瞬间几乎必然为空。更糟的是,它不记录历史,也无法区分是人工登录还是程序连接。曾有人用它写自动清理脚本,结果把所有用连接池复用账号的应用全干掉了——因为池子里的连接是长连,PROCESSLIST 里永远有它们,但业务账号本身早已下线。
真正稳定的判断逻辑只能是:日志有记录 + 配置已下线 + 权限已回收。三者缺一不可。任何想绕过日志、单靠元数据表“猜”活跃性的做法,最终都会变成删库跑路的前奏。











