MySQL 8.0+ 无内置用户操作审计日志,password_last_changed仅记录部分密码修改时间且不显示操作者;需依赖general_log(查SQL文本)或audit_log插件(含user_host、command_class等结构化字段)实现追溯。

MySQL 8.0+ 没有内置的“用户操作审计日志”
直接查 mysql.user 表只能看到当前密码哈希和权限状态,看不到谁、什么时候改过密码或授权。MySQL 默认不记录这类操作的历史——除非你提前开启了审计功能。很多运维同学卡在这一步,以为 SELECT * FROM mysql.user 能看出修改时间,其实 password_last_changed 字段只反映该账户最后一次设置密码的时间(仅限 MySQL 8.0+),且它不记录“谁执行的”,也不记录 GRANT/REVOKE 操作。
启用 general_log 或 audit_log 才能追溯修改行为
如果还没开启日志,现在补救也来得及,但要注意:日志是“事后”记录,无法回溯已发生的操作。实际可用路径只有两条:
-
general_log:记录所有 SQL 语句(含
ALTER USER、SET PASSWORD、GRANT、REVOKE),开销小但无结构化用户/操作类型字段;需设general_log = ON,日志输出到文件或表(推荐log_output = 'TABLE',便于用SELECT查询) -
audit_log(企业版或插件版):专为审计设计,字段明确含
userhost、command_class(如alter_user、grant)、timestamp;但社区版默认不带,需手动安装插件audit_log.so并配置audit_log_policy = ALL
示例(查 general_log 表中最近的权限变更):
SELECT event_time, user_host, argument FROM mysql.general_log WHERE command_type = 'Query' AND argument REGEXP '^(ALTER USER|SET PASSWORD|GRANT|REVOKE)' ORDER BY event_time DESC LIMIT 20;
password_last_changed 只适用于密码修改,且有前提
这个字段只在 MySQL 8.0+ 生效,且仅当使用 ALTER USER ... IDENTIFIED BY 或 SET PASSWORD 修改密码时自动更新;用 UPDATE mysql.user 直接改 authentication_string 不会触发更新,还可能破坏账户状态。
- 必须确保账户使用
caching_sha2_password或sha256_password插件(老的mysql_native_password不支持该字段) - 查询方式:
SELECT User, Host, password_last_changed FROM mysql.user WHERE password_last_changed > DATE_SUB(NOW(), INTERVAL 7 DAY);
- 注意:
password_last_changed是TIMESTAMP类型,但值可能为NULL(初始账户未设密码)或'0000-00-00 00:00:00'(表示未知)
真正可靠的方案:从备份或 binlog 中提取操作痕迹
如果没开任何日志,又必须定位某次修改,唯一可行的是解析 binlog——前提是 binlog_format = ROW 或 STATEMENT 且保留了足够久的 binlog 文件。
mysqlbinlog --base64-output=DECODE-ROWS -v mysql-bin.000001 | grep -A 2 -B 2 'ALTER USER\|GRANT\|REVOKE'- 注意:
ROW格式下 DDL(如ALTER USER)仍以STATEMENT记录,所以能搜到;但权限变更本质是写mysql.db、mysql.tables_priv等系统表,ROW日志里会有对应UPDATE或INSERT事件 - 风险点:binlog 不记录执行者 IP 或账号名(只记录发起连接的
user@host),若用 root 远程批量操作,就难以区分具体责任人
没有预设日志机制的情况下,靠临时翻日志或 binlog 查“谁改了权限”,本质上是在拼运气和保留周期。下次部署新实例时,至少把 general_log 开在表里,设好轮转策略——不然问题来了,只能看监控告警时间戳去倒推。


















