先抛一个实际痛点:在 Go 应用里直接用 MySQL 的 CREATE TEMPORARY TABLE,看似顺理成章,实则一踩一个准——临时表只对当前数据库连接会话可见,而 Go 的 database/sql 连接池是随机复用连接的。你前脚刚创建的临时表,后脚另一个请求可能就拿到不同的连接,结果就是 SELECT 什么也查不到。

有人会说:用事务强制绑定连接不就行了?理论上确实可行——通过 db.Begin() 将创建和查询塞进同一个事务里,就能保证复用同一条连接。但这个方案有显著的副作用:长事务意味着连接长时间不归还给池子,并发上来以后,连接池很快被堵死;同时事务本身会持有锁,加剧竞争,吞吐量直线下滑。说白了,事务是为「短、快、原子」而生的,拿它当中间结果缓存容器,属于用错了工具。

那么,有没有更优雅的解法?命名内存表(MEMORY 引擎的表) 才是真正的答案。它全局可见(跨任何连接),内存存储(读写极快),而且生命周期完全由你控制——创建、使用、销毁,条理清晰。完美适配 Go 无状态的连接模型:

-- 创建一张唯一命名的内存表,建议带上时间戳或 UUID 前缀
CREATE TABLE mydb.temp_20240520_abc123 (
  id BIGINT PRIMARY KEY AUTO_INCREMENT,
  user_id INT,
  order_amount DECIMAL(10,2),
  created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
) ENGINE=MEMORY;
// Go 中全流程:创建 → 填充 → 查询 → 清理
_, err := db.Exec("CREATE TABLE mydb.temp_20240520_abc123 (...) ENGINE=MEMORY")
if err != nil { /* handle */ }

_, err = db.Exec("INSERT INTO mydb.temp_20240520_abc123 SELECT ... FROM large_table WHERE ...")
if err != nil { /* handle */ }

rows, err := db.Query("SELECT u.name, t.order_amount FROM users u JOIN mydb.temp_20240520_abc123 t ON u.id = t.user_id")
// ... 处理结果

_, err = db.Exec("DROP TABLE mydb.temp_20240520_abc123") // 一定别忘了清理

关键细节列出来,少一个都可能出问题:

说到底,与其死磕 Go 不支持会话级临时表的事实,不如换个思路:用带生命周期管理的命名表,把临时性做得更彻底、更可控。这在业界已经是经过验证的成熟模式,尤其适合复杂查询的中间结果缓存场景。

本文转载于:https://www.php.cn/faq/2312747.html 如有侵犯,请联系zhengruancom@outlook.com删除。
免责声明:正软商城发布此文仅为传递信息,不代表正软商城认同其观点或证实其描述。