MySQL本身不支持基于时间的高危操作限制,必须通过外部方案实现:一是用ALTER USER ACCOUNT LOCK配合cron定时开关账号;二是用ProxySQL在连接时校验时间并拒绝非授权时段请求。

MySQL 本身不支持对 DROP、TRUNCATE、ALTER 等高危操作设置时间窗口限制——权限系统里没有 GRANT ... IF HOUR(NOW()) BETWEEN 9 AND 17 这种语法,也没法在 CREATE USER 或触发器中嵌入运行时时间判断。真要实现“只在工作时间才能删表”,得靠外部调度 + 权限动态开关,或代理层拦截。
用 ACCOUNT LOCK + cron 定时控制账号可用性
这是最轻量、兼容 MySQL 5.7.6+ 和 8.0 的方案:把高危操作账号当作“门禁卡”,每天固定时间锁/解锁。
- 必须用
ALTER USER ... ACCOUNT LOCK(不是UPDATE mysql.user),否则FLUSH PRIVILEGES不生效 - 脚本里禁止明文写密码,用
~/.my.cnf配置 root 凭据,并设chmod 600 - 锁定后用户连接会报错:
ERROR 3118 (HY000): Access denied for user 'dba_dev'@'10.1.2.3'. Account is locked. - crontab 示例(周一至五早 9 点解锁、晚 6 点锁定):
0 9 * * 1-5 /usr/local/bin/unlock-dba.sh<br>0 18 * * 1-5 /usr/local/bin/lock-dba.sh
- 注意:MySQL 自带的
EVENT调度器不可靠,它不保证准时执行,且重启后可能丢失状态
用 ProxySQL 在连接时校验时间并拒绝
如果你已在用 ProxySQL 做流量路由,这是更实时、可绕过账号锁的方案:把时间判断逻辑前置到连接建立阶段。
- ProxySQL v2.4+ 支持
mysql_query_rules的apply+error_msg字段,能根据后端查询结果直接断开连接 - 配置一条规则匹配所有连接(如
match_pattern = '^.*$'),指向一个专用 hostgroup - 该 hostgroup 后端跑一个轻量查询:
SELECT IF(HOUR(NOW()) BETWEEN 9 AND 17, 1, 0) AS ok - 当返回
ok = 0,ProxySQL 返回自定义错误并终止连接,用户看到的是Access denied outside business hours - 缺点:需维护 ProxySQL 实例;排查连接失败时,得先查 ProxySQL 日志,而不是 MySQL 错误日志
为什么不能用触发器或存储函数拦截登录时间?
因为 MySQL 登录认证流程根本不经过 SQL 层——用户还没拿到会话,就已完成身份验证和权限检查。触发器只响应 INSERT/UPDATE/DELETE,CREATE USER 里的 WHERE 是主机匹配语法,不是条件表达式。
-
CREATE USER 'u'@'%' WHERE HOUR(NOW()) = 9;→ 语法错误,WHERE此处不合法 - 试图在
mysql.user表里加时间字段再触发更新?不行,mysql.user是系统表,不能加自定义列 - 应用层连上后再
KILL CONNECTION_ID()?可以作为补充,但无法防止直连 MySQL 的绕过行为 - Linux PAM
pam_time.so只对本地 socket 有效,且要求 MySQL 以非mysql系统用户运行——生产环境几乎不可行
真正落地时,最容易被忽略的是权限回收链路:比如你锁了 dba_dev 账号,但它所属的角色 ddl_role 还有 DROP 权限,或者它通过 'dba_dev'@'localhost' 和 'dba_dev'@'%' 两个 Host 条目存在,只锁了一个,另一个仍可连。务必用 SHOW GRANTS FOR 'dba_dev'@'%'; 和 SHOW GRANTS FOR 'dba_dev'@'localhost'; 分别确认。


















