INVISIBLE列仅绕过SELECT *和无字段列表的INSERT,但可被显式DML操作(如INSERT/UPDATE/REPLACE指定列名)、触发器、备份复制等完全访问;它不提供写保护,本质是元数据隐藏而非权限控制。

MySQL 8.0 没有原生的“不可写列”机制,INVISIBLE 列不是只读列,它只是默认不参与 SELECT * 和普通 INSERT(无显式字段列表)——但它完全可写、可更新、可被显式引用。
INVISIBLE 列会被哪些操作绕过?
很多人误以为设为 INVISIBLE 就等于“锁住字段”,实际它只影响默认行为,不提供写保护:
-
INSERT INTO t2 (name, age) VALUES ('Alice', 25)—— 显式列出age,插入成功 -
UPDATE t2 SET age = 30 WHERE id = 1—— 直接更新,无任何拦截 -
SELECT id, name, age FROM t2—— 显式指定字段,age照常返回 -
REPLACE INTO、INSERT ... ON DUPLICATE KEY UPDATE同样可操作该列
为什么不能用 INVISIBLE 实现只读?
INVISIBLE 的设计目标是“隐藏元数据”,不是“访问控制”。它的语义是:让客户端在不感知的情况下忽略该列,而非阻止 DML。关键事实:
- 权限系统(
GRANT)无法按列限制UPDATE或INSERT,只支持SELECT级别列授权 - 触发器(
BEFORE UPDATE/BEFORE INSERT)才是唯一能拦截写入的手段 -
ALTER TABLE ... MODIFY ... READONLY是无效语法 —— MySQL 不支持该关键字,执行会报错ERROR 1064 (42000)
真正可行的列级写保护方案
若业务强需求某列“绝对不可修改”,必须组合使用以下两种方式:
- 创建
BEFORE UPDATE触发器,检查是否修改了目标列,若修改则SIGNAL SQLSTATE '45000'抛出错误 - 创建
BEFORE INSERT触发器,对目标列值做校验(如强制为DEFAULT或特定值),否则拒绝插入 - 配合权限控制:只授予应用用户
SELECT权限,不授INSERT/UPDATE;由存储过程封装写逻辑,过程内做字段白名单校验
示例触发器(防止更新 created_at):
DELIMITER $$
CREATE TRIGGER prevent_created_at_update
BEFORE UPDATE ON t2
FOR EACH ROW
BEGIN
IF OLD.created_at != NEW.created_at THEN
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = 'created_at is immutable';
END IF;
END$$
DELIMITER ;
INVISIBLE 列真正的适用场景
它适合做“后台字段”而非“防护字段”,比如:
- 记录行级版本号(
row_version INT INVISIBLE DEFAULT 0),业务 SQL 不用改,但应用层可通过显式查询获取 - 标记数据来源(
source_flag TINYINT INVISIBLE DEFAULT 1),避免污染通用查询结果 - 灰度字段(
new_logic_flag BOOLEAN INVISIBLE DEFAULT FALSE),上线前不暴露给旧服务
注意:INVISIBLE 列仍占用存储空间、参与索引、影响性能,且备份/恢复、主从复制完全透明 —— 它不是轻量级开关,而是结构层变更。


















