只给SELECT权限可禁用UPDATE/DELETE:MySQL不支持字段级UPDATE权限,需用视图或触发器替代;REVOKE须严格匹配原授权四元组,否则静默失败;权限变更需用户重连生效。

只给SELECT权限,不给UPDATE/DELETE:最直接的权限控制
MySQL本身不支持“禁止UPDATE但允许INSERT”的细粒度表级操作限制——GRANT语句里,UPDATE和DELETE是独立权限项,可以单独关闭。只要不显式授予,就默认没有。
- 新建用户后,只执行:
GRANT SELECT ON <code>db_name</code>.<code>table_name</code> TO 'user'@'%';
- 切勿执行包含
UPDATE、DELETE或ALL PRIVILEGES的授权 - 权限生效前记得运行
FLUSH PRIVILEGES;(仅在手动改了mysql系统表时才必须;用GRANT通常自动刷新)
常见错误现象:用SHOW GRANTS FOR 'user'@'%';查到结果里有 UPDATE,说明之前误授过,得用REVOKE UPDATE, DELETE ON ... FROM ...;收回。
误授了UPDATE/DELETE权限,怎么安全回收?
回收权限不是“删除再重建用户”,而是精准撤销。重点在于作用域必须完全匹配当初授权时的范围(库名、表名、host)。
- 原授权是:
GRANT UPDATE ON <code>sales_db</code>.<code>orders</code> TO 'reporter'@'10.20.%';
- 回收就必须写:
REVOKE UPDATE ON <code>sales_db</code>.<code>orders</code> FROM 'reporter'@'10.20.%';
- 少一个反引号、库名大小写不一致、host写成
'%'而非实际值,都会导致REVOKE静默失败(不报错,但权限还在)
性能影响几乎为零:权限检查发生在语句解析阶段,开销可忽略。但要注意,如果用户已建立长连接,回收后需其断开重连才能生效——已有连接仍保留旧权限缓存。
想让某些字段可更新、某些字段不可更新?MySQL原生不支持
MySQL没有COLUMN级别UPDATE权限(如“只允许改status,禁止改amount”)。这是常见误解点。
- 可行替代方案只有两个:
- 用视图封装,只暴露可更新字段,并加
WITH CHECK OPTION约束 - 在应用层或存储过程里做字段级校验(比如触发器拦截非法UPDATE,抛出
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = 'Field amount is read-only';)
- 用视图封装,只暴露可更新字段,并加
注意:触发器无法阻止UPDATE ... SET @var := amount这类间接读取,且对批量UPDATE性能有明显拖累。视图方案更轻量,但要求所有写入都走视图,不能直连基表。
为什么REVOKE后还是能删数据?检查这三点
权限没生效,90%是因为以下某个环节出问题:
- 用户连接用的是其他账号(比如应用配置写死
root,你改的是app_user) - 授权对象写错了:比如对
db.*授了权,但忘了REVOKE是针对db.table的,范围不匹配 - 存在更高优先级权限:用户同时有
root角色,或被授予了ALL ON <em>.</em>,这种全局权限会覆盖表级REVOKE
验证方式很简单:
SELECT user, host, select_priv, update_priv, delete_priv FROM mysql.tables_priv WHERE db='your_db' AND table_name='your_table';
看结果里
update_priv和delete_priv是否为N。别只信SHOW GRANTS——它只显示GRANT语句,不反映REVOKE后的最终状态。
权限系统看着简单,实际依赖库名、表名、host、用户名四元组精确匹配,漏掉一个反引号或大小写,就等于没操作。










