根本原因是数据库执行路径失控导致内存野蛮索取:MySQL视图含GROUP BY/DISTINCT等强制物化、PG/SQL Server视图ORDER BY不被下推、嵌套视图引发基数误估、SELECT *触发大字段落盘、游标未CLOSE长期占用内存。

SELECT * 从复杂视图查数据爆 Out of memory 或 ERROR 2013: Lost connection,90% 不是内存真不够,而是数据库被迫把几百万行全载入内存再排序/分组/JOIN。调 sort_buffer_size 或 work_mem 只会让崩溃来得更慢、更隐蔽。
MySQL 视图含 GROUP BY/DISTINCT 就强制物化
视图定义里只要出现 GROUP BY、DISTINCT、UNION 或子查询,MySQL 就放弃 MERGE 优化,改走 DERIVED 物化路径——整个中间结果集先生成临时表,再处理。
-
EXPLAIN SELECT * FROM my_view LIMIT 10显示select_type = DERIVED,且rows接近底层表总行数(比如 500 万),说明已全量加载 -
SHOW STATUS LIKE 'Created_tmp_disk_tables'持续上涨,且与Created_tmp_tables比值 >15%,基本确认落盘 - 哪怕你只想要 10 条,它也得先把几百万行读进内存排一遍,
sort_buffer_size再大也救不了
PostgreSQL / SQL Server 视图里的 ORDER BY 不下推
视图定义里写了 ORDER BY created_at DESC,外层再加 LIMIT 10,优化器大概率不识别、不合并——结果就是:全量排序 → 再截断 → 内存吃满。
-
EXPLAIN (ANALYZE, BUFFERS) SELECT * FROM my_view LIMIT 10看Plan Rows:如果远大于 10,说明没下推 - SQL Server 的
sys.dm_db_task_space_usage查到task_allocations > 10000(约 80MB)且sql text含SELECT * FROM dbo.vw_sales_summary,基本坐实全排 - 唯一安全做法:删掉视图里的
ORDER BY,把排序移到最外层,且确保字段(如id)有索引
嵌套视图 + JOIN 大表引发基数误估
视图 A 引用视图 B,B 里有 DISTINCT,A 再跟千万级表 JOIN ——优化器可能把本该 1 万行的结果膨胀成 50 万行,导致内存 grant 不足,被迫 spill 到 TempDB。
-
SET STATISTICS XML ON执行后看执行计划,找Sort或Hash Match节点的SpillLevel="1"或警告文字Operator used tempdb to spill data - 避免在视图里写
SELECT *,尤其避开TEXT、BLOB字段;它们会让临时表直接落盘 - 为所有
JOIN、ORDER BY、WHERE字段建联合索引,例如CREATE INDEX idx_status_time ON orders (status, created_at)
别信“过程结束自动释放”,游标和 sp_head::main_mem_root 是真坑
MySQL 存储过程中用 DECLARE CURSOR 查视图,若没显式 CLOSE,内存就永远挂着——这块 sp_head::main_mem_root 不走 innodb_buffer_pool_size 管控,也不会随过程退出释放。
- 查
performance_schema.memory_summary_global_by_event_name,盯memory/sql/sp_head::main_mem_root:超 1GB 基本就是它 - 每个
DECLARE CURSOR cursor_name后必须紧跟着CLOSE cursor_name,且异常路径也要覆盖:DECLARE CONTINUE HANDLER FOR SQLEXCEPTION BEGIN CLOSE cursor_name; LEAVE proc_label; END; - 大表关联别硬
JOIN,改用主键分段拉取:SELECT id FROM orders WHERE id > ? ORDER BY id LIMIT 1000,再用这批id精准查右表
真正卡住的从来不是物理内存大小,而是数据库执行路径失控时对内存的野蛮索取——一个没索引的 ORDER BY、一层没过滤的嵌套、一个没 CLOSE 的游标,都足以让几 MB 的缓冲瞬间崩塌。

















