两个 MySQL 窗口都连着同一个实例、同一个库执行的也是同一句查询SELECT label FROM blog_scope_item; 窗口Atemporary-A 窗口Bpermanent先不往“隔离级别”上猜。这个例子里没有未提交的数据也不需要构造快照差异。窗口 A 比 B 多做了一件事创建了一张同名临时表。旧的 InnoDB 提纲里临时表、临时表空间和内部临时表放在一起容易记成一串名字。这次先只看我们自己用CREATE TEMPORARY TABLE创建的表搞清楚它属于哪个连接、什么时候消失。先让两个窗口看到同一张普通表下面在独立测试实例里做不要用业务库。新建的库名如果已经存在就换一个名字并同步改后面的USE不要删除已有库来照抄实验。本次实际环境是 MySQL 8.0.46新建 A、B 两个持续连接均使用默认的autocommit1。每一小段按标注的连接执行不要把 SQL 工具的两个编辑页签直接当作两个连接可以先用SELECT CONNECTION_ID()核对它们是否不同。在 A 创建专用实验库和一张普通表CREATE DATABASE blog_temp_scope_lab; USE blog_temp_scope_lab; CREATE TABLE blog_scope_item ( id INT PRIMARY KEY, label VARCHAR(32) NOT NULL ) ENGINEInnoDB; INSERT INTO blog_scope_item VALUES (1, permanent); SELECT label FROM blog_scope_item;输出permanent。B 也选中这个库USE blog_temp_scope_lab; SELECT label FROM blog_scope_item;同样是permanent。目前两个连接访问的是同一张普通表。A建一张同名临时表普通表被覆盖了吗在 A 执行CREATE TEMPORARY TABLE blog_scope_item ( id INT PRIMARY KEY, label VARCHAR(32) NOT NULL ) ENGINEInnoDB; INSERT INTO blog_scope_item VALUES (1, temporary-A); SELECT label FROM blog_scope_item;这次得到temporary-A。注意这里没有清空普通表也没有用新数据更新它。MySQL 允许临时表与同库中的普通表同名对 A 而言后续这个名字先解析到它自己的临时表。连接A查blog_scope_item | v A的同名临时表 temporary-A 普通表permanent仍在 只是这个名字在A中先指向了临时表现在去 B 查SELECT label FROM blog_scope_item;它仍然得到permanent。所以不是普通表的数据被改了也不是 A 的临时数据尚未提交给 B。这张临时表只对创建它的会话可见。A、B 用同一个名字最终指向的对象可以不同。MySQL 临时表说明B也建一张同名临时表呢既然 A 的临时表对 B 不可见B 也可以使用这个名字CREATE TEMPORARY TABLE blog_scope_item ( id INT PRIMARY KEY, label VARCHAR(32) NOT NULL ) ENGINEInnoDB; INSERT INTO blog_scope_item VALUES (1, temporary-B); SELECT label FROM blog_scope_item;得到temporary-B。这时再在 A 查询SELECT label FROM blog_scope_item;仍然是temporary-A。同一个库名、同一个表名现在实际上对应三份对象A自己的临时表 - temporary-A B自己的临时表 - temporary-B 普通表 - permanent A查名字 - A的临时表 B查名字 - B的临时表如果只拿着库名、表名比对两个人都可能觉得“我查的肯定就是你那张”。这里还得看会话。删掉临时表后为什么原来的数据又出现了在 A 明确删除临时表再查同一个名字DROP TEMPORARY TABLE blog_scope_item; SELECT label FROM blog_scope_item;结果变回permanent。它不是从回收站“恢复”了也没有把临时表回滚成旧版本。普通表一直在那里只是挡在前面的同名临时表被移走了删除前A查名字 - 临时表 删除后A查名字 - 普通表 普通表本身没有被改动B 那边又怎样SELECT label FROM blog_scope_item;还是temporary-B。A 只能删掉自己会话里的那一张B 的临时表不受影响。这里特意写DROP TEMPORARY TABLE不是随手用DROP TABLE。前者把意图限制到临时表在有同名普通表时这个区分很重要不能把“不存在时再试一次删除”当成无害操作。临时表是跟事务走还是跟连接走还有一个容易想当然的地方既然叫临时表事务结束就该没了吧换一个不与普通表重名的名字在 A 做下面这组操作START TRANSACTION; CREATE TEMPORARY TABLE blog_tx_temp ( id INT PRIMARY KEY ) ENGINEInnoDB; INSERT INTO blog_tx_temp VALUES (1); ROLLBACK; SELECT COUNT(*) FROM blog_tx_temp;结果不是“表不存在”而是0。这里发生了两件需要分开看的事创建临时表 - 表存在 插入一行 - 表里有1行 ROLLBACK - 插入回滚剩0行 临时表本身仍在本例使用 InnoDB所以这次插入的数据可以回滚。CREATE TEMPORARY TABLE本身不因ROLLBACK被撤销它也不像许多普通 DDL 那样隐式提交当前事务。不要把“没有隐式提交”和“这个DDL能回滚”混成一件事。MySQL 隐式提交说明继续在同一个 A 连接里插入、查询INSERT INTO blog_tx_temp VALUES (2); SELECT id FROM blog_tx_temp; DROP TEMPORARY TABLE blog_tx_temp;能查到2。这也再次说明前一次事务结束没有结束这张临时表的生命周期。真正结束连接才是另一回事现在让 B 断开再建立一个新的连接 C选中同一个库执行USE blog_temp_scope_lab; SELECT label FROM blog_scope_item;会得到permanent。B 的临时表在原会话结束后自动删除新连接没有继承它。这不是让 C 去访问 B 的私有表C 解析这个名字时没有自己的临时表就访问普通表。实验中还另外核对了 B 的会话确实已经结束而不是只关了一个编辑页签。这对连接池也有影响。业务代码调用“关闭”可能只是把连接归还池里没有结束 MySQL 的物理会话。若驱动或连接池没有做会话重置、清理临时表就不能仅凭“这次请求结束了”被认定已消失。这里具体要核对连接池的重置策略不能说所有池都会泄漏也不能说所有池都会自动替你清理。用完后在 B 断开前主动删除自己创建的临时表或者在应用里明确安排清理比依靠它名字里那个“临时”更可靠。清理对象也应是自己建立的实验表不要对未知同名表盲删。我们这次说的不是所有“临时表”以上全部是显式CREATE TEMPORARY TABLE创建的表。优化器为了排序、聚合等操作建立的内部临时表是另一个话题不能把它们的创建方式、可见性和控制参数直接套过来。同样“临时表”不等于“数据永远只在内存”。本例明确指定了 InnoDB不能拿它当 MEMORY 引擎的实验更没有通过前面的查询验证数据文件的具体布局。这次先把会话作用域、同名遮蔽、事务回滚三件事讲清。这组SQL怎么验证我用独立数据目录启动 MySQL 8.0.46只通过本地套接字访问不连接业务数据库。程序保持 A、B 两个客户端进程按正文顺序把每一段 SQL 发到对应连接再逐段核对输出。到“断开 B”时程序真正结束 B 的客户端再创建 C不是在同一个连接里换个变量名。还用原 B 的连接编号检查它已经不在进程列表中之后才检查 C 的结果。对于回滚那一节另外补了一个对照在普通实验表中先更新再创建临时表然后回滚确认普通表的更新也撤销了。这是在检查“临时表DDL没有偷偷提交前面的更新”而不只是发现临时表还存在就推断事务如何处理。最后这些用例不能证明某个真实项目里的连接池一定怎样工作它们给出的是你检查自己项目时可以分开观察的状态和动作。
阅读完成 · 觉得有帮助?