声明varchar(65535)会直接预占64kb栈空间,10个即超默认256kb thread_stack,引发栈溢出;declare是静态预留、全程驻留,而@var是动态堆分配、可复用释放。

存储过程里声明 VARCHAR(65535) 会直接吃掉大量内存
不是“用的时候才分配”,而是声明即预占——MySQL 在解析存储过程时,会为每个 DECLARE 的变量按最大可能长度预留栈空间。比如 DECLARE msg VARCHAR(65535),哪怕你只赋值 'OK',InnoDB 仍按 65535 字节(≈64KB)在当前会话的线程栈中划出一块连续内存。10 个这样的变量,光变量声明就占掉 640KB 栈空间,而 MySQL 默认 thread_stack 才 256KB,极易触发 Stack overflow 或静默截断。
SET @var := 'xxx' 和 DECLARE var VARCHAR(N) 内存行为完全不同
前者是动态分配、按需增长;后者是静态预留、固定上限。关键区别在于:
-
DECLARE变量生命周期绑定存储过程作用域,全程驻留线程栈,无法被 GC 或回收 -
@var是会话级用户变量,底层走 heap 分配,用完可被复用或释放 - 游标遍历中若在循环内
DECLARE temp VARCHAR(10000),每次迭代都新占栈空间,不释放——不是“覆盖”,是“叠加”
查当前存储过程实际内存开销:看 INFORMATION_SCHEMA.PROCESSLIST + SHOW ENGINE INNODB STATUS
单个存储过程执行时的内存压力,没法靠 SELECT 查出来,但可通过两个线索交叉验证:
- 查
PROCESSLIST中该线程的STATE是否长期卡在executing或Copying to tmp table,同时INFO字段显示含大变量操作 - 运行
SHOW ENGINE INNODB STATUS\G,搜ROW OPERATIONS下的memory allocated值,若单次调用飙升数百 MB,基本锁定是大变量或未释放的临时表 - 注意:
innodb_buffer_pool不计入此——那是全局缓存,和存储过程栈内存无关
真正安全的写法:按需声明 + 显式清空 + 避免嵌套作用域滥用
别指望“反正我只用一次”,存储过程的内存是硬预留、不释放的。稳妥做法只有三条:
- 声明前先
SELECT MAX(CHAR_LENGTH(col)) FROM t WHERE ...算出真实最大长度,+20% 安全余量,而不是拍脑袋写VARCHAR(4000) - 用完大变量后,显式
SET var = NULL(对DECLARE无效,但能减少后续误用风险;对@var有效) - 绝对不要在
WHILE或REPEAT循环里DECLARE大字符串变量——改用SET @temp = ...,或把逻辑拆成独立小过程
最易被忽略的一点:MySQL 存储过程没有自动作用域回收机制,BEGIN...END 块内声明的变量,只要过程没结束,就一直占着线程栈——哪怕你已经 LEAVE 出了那个块。











