权限不足时,LOCK TABLES
会直接报错
不是所有用户都能加表锁。MySQL 要求执行
LOCK TABLES必须拥有
LOCK TABLES权限(属于全局权限),且不能是只读账号(
read_only=1时 super 用户仍可锁,但普通用户会被拒绝)。常见错误:
ERROR 1045 (28000): Access denied for user 'app'@'%' to database 'test'—— 实际不是库权限问题,而是缺少
LOCK TABLES权限。 检查权限:运行
SHOW GRANTS FOR CURRENT_USER;,确认输出中包含
LOCK TABLES授予权限(需 super 或 grant option):
GRANT LOCK TABLES ON *.* TO 'app'@'%'; FLUSH PRIVILEGES;注意:MySQL 8.0+ 中,
LOCK TABLES权限不再隐含在
SELECT或
UPDATE中,必须显式授予 即使有
LOCK TABLES权限,若当前连接处于事务中(
AUTOCOMMIT=0),执行
LOCK TABLES会自动触发隐式提交,导致事务中断——这是极易被忽略的副作用
FLUSH TABLES WITH READ LOCK
需要 RELOAD
权限,且绕过 read_only
限制
全局读锁命令
FLUSH TABLES WITH READ LOCK不同于普通表锁,它要求
RELOAD权限(而非
LOCK TABLES),并且能强制阻塞所有写入——哪怕你已设
read_only=1,这个命令仍会让 super 用户也无法写,因为它是物理级阻塞。 验证权限:
SELECT Reload_priv FROM mysql.user WHERE User='root' AND Host='%';返回
Y才有效 执行后,任何 DML(
INSERT/
UPDATE/
DELETE)、DDL(
CREATE/
DROP)都会挂起等待,包括 root 用户 解锁必须用
UNLOCK TABLES,且只能由同一连接执行;断开连接会自动释放锁,但可能导致备份不一致 该命令会阻塞
mysqldump --single-transaction的一致性快照起点,所以不要在备份脚本里混用
InnoDB 行锁不受用户权限控制,但权限影响能否触发行锁
行锁(如
SELECT ... FOR UPDATE)本身不校验权限——只要语句能执行成功,InnoDB 就加行锁。但“能否执行成功”取决于权限:比如没
SELECT权限,连查询都失败,自然无法加锁;没
UPDATE权限,
SELECT ... FOR UPDATE可以执行(因只读),但后续
UPDATE会报错。 典型陷阱:给应用账号只授
SELECT权限,却让它执行
SELECT ... FOR UPDATE—— 看似能跑通,但实际锁住了行,而业务逻辑又没后续更新,导致锁长期滞留 行锁是否生效,还依赖索引:若
WHERE条件未命中索引,InnoDB 会升级为表锁(甚至全表扫描锁),此时又回到权限和锁粒度双重风险 权限越精细,越容易误判锁行为:例如对某列有
SELECT权限但无
UPDATE权限,
SELECT col1 FROM t WHERE id=1 FOR UPDATE成功加锁,但
UPDATE t SET col2=1 WHERE id=1直接报错
ERROR 1142 (42000): UPDATE command denied,锁却没释放
备份/维护场景下,权限与锁的组合最容易出事
线上做逻辑备份、主从切换或数据迁移时,常同时涉及权限变更和锁操作。这时权限配置稍有偏差,就会让锁失效或过度阻塞。
例如用mysqldump --lock-all-tables:它内部执行
FLUSH TABLES WITH READ LOCK,所以 dump 账号必须有
RELOAD权限,否则报错退出,但 dump 进程可能已部分写入脏文件 再如用
pt-online-schema-change:它依赖
SELECT/
INSERT/
UPDATE/
DELETE和
TRIGGER权限,若漏授
TRIGGER,工具会退化为锁表方式,导致业务阻塞 最隐蔽的是权限缓存:修改了
mysql.user表但忘了
FLUSH PRIVILEGES,新权限不生效,锁命令看似执行成功,实则未真正加锁(尤其在 MySQL 5.7 及更早版本)
权限本身不控制锁的类型或范围,但它决定了你“有没有资格发起锁操作”。真正危险的不是锁不住,而是锁住了却没人意识到——比如一个只有
SELECT权限的账号执行了
FOR UPDATE,锁持续到事务结束,而 DBA 查权限时只看 DML 权限,完全忽略这种“只读但带锁”的中间态。
