create temporary table在只读事务中不报错,因其仅创建会话级临时对象,不修改全局数据字典、不产生redo/binlog,且官方明确允许;后续对临时表的insert等操作也允许,但仅限当前会话,且不加锁、不参与mvcc和事务锁机制。

可以执行,但临时表创建本身不触发行级锁,也不受事务只读限制;不过后续对临时表的写操作仍会受SET TRANSACTION READ ONLY约束。
为什么CREATE TEMPORARY TABLE在只读事务中不报错
MySQL 的只读事务(通过 SET TRANSACTION READ ONLY 或 START TRANSACTION READ ONLY 启用)仅限制对**持久表(non-temporary base tables)** 的写入,包括 INSERT、UPDATE、DELETE、REPLACE、DDL 等操作。而 CREATE TEMPORARY TABLE 是一个会话级元数据操作,它:
- 不修改任何全局数据字典或持久表结构
- 不产生 redo log 或 binlog(除非显式开启
log_bin_trust_function_creators且涉及函数,但临时表本身不记 binlog) - 只在当前会话的内存和临时目录中建立对象,作用域天然隔离
因此,它被明确排除在只读事务的禁止列表之外——官方文档也注明:“CREATE TEMPORARY TABLE is permitted in read-only transactions.”
SELECT ... FOR UPDATE或UPDATE对临时表是否加锁
完全不加锁。InnoDB 的行锁、间隙锁、临键锁等机制,全部针对**聚簇索引和二级索引所在的持久表**。临时表在 InnoDB 中有两种实现路径:
- 内存引擎临时表(
ENGINE=MEMORY):无锁,纯内存哈希/树结构,不走 InnoDB 锁子系统 - InnoDB 临时表(
ENGINE=InnoDB,5.7+ 默认):虽用 InnoDB 存储,但其数据字典条目标记为TEMPORARY,所有 DML 绕过事务锁队列、MVCC 版本链和锁监控视图(如INFORMATION_SCHEMA.INNODB_TRX、INNODB_LOCKS)
换句话说:SELECT * FROM tmp_table FOR UPDATE 在语法上合法,但实际不获取任何锁,也不会阻塞其他会话——因为其他会话根本看不到这个临时表。
容易忽略的关键点:只读事务里建了临时表,后续INSERT INTO tmp是否允许
允许,但有前提:
- 如果事务是
START TRANSACTION READ ONLY显式开启,则后续对临时表的INSERT、UPDATE、DELETE全部允许 - 但如果事务由
SET SESSION TRANSACTION READ ONLY设置,且尚未START TRANSACTION,此时直接INSERT INTO tmp会隐式启动一个只读事务 —— 这个隐式事务仍允许操作临时表 - 真正被禁止的是:向普通表(如
orders)写入,或执行DROP TABLE orders这类影响持久对象的操作
注意:TRUNCATE TEMPORARY TABLE 也允许,但 DROP TEMPORARY TABLE 实际上不是 DDL,而是会话清理动作,不参与事务控制。
生产环境踩坑提醒
最常被忽略的一点是:临时表内容**不会被复制到从库**,且在主从切换后原会话重建时自动丢失。如果你在只读事务中依赖临时表做中间计算(比如 JOIN 缓存结果),要确保逻辑不假设该表跨事务或跨会话存在;另外,SHOW PROCESSLIST 里看不到临时表操作的锁等待,别误以为“没锁就安全”——它只是根本不在锁体系里,和“锁得快”是两回事。











