发送带发票 PDF 或自定义报表的邮件时出现 Allowed memory size exhausted。附件的二进制数据、Base64 编码、MIME 消息和 PDF 渲染可能同时驻留内存,实际峰值远高于文件大小;简单提高 memory_limit 可能把问题推向并发高峰。

先固定复现条件和影响范围

用测试订单记录 PDF 原始大小、生成耗时、发送进程 memory_get_peak_usage、附件数量和是否异步。比较无附件、单个小附件与问题附件,不在日志保存文档内容或客户信息。

php -i | grep memory_limit
du -h var/tmp/*.pdf 2>/dev/null | sort -h | tail
grep -R --line-number 'addAttachment|createAttachment|TransportBuilder' app/code 2>/dev/null
grep -RniE 'Allowed memory size|out of memory|email' var/log | tail -n 80
php bin/magento config:show sales_email/general/async_sending

根据证据分支定位

生成 PDF 前就耗尽,查商品图片和模板循环;PDF 已生成、构建 MIME 时耗尽,检查整文件多次复制和 Base64;只在队列失败,检查消息是否携带完整二进制而非可重建引用。重复附件还可能来自 Plugin 多次调用。

修复时保留回滚路径

限制附件数量和尺寸,减少高分辨率图片,避免在多个字符串变量中复制同一二进制。队列消息保存受控文件引用或业务 ID,并在消费时验证权限和生命周期;临时文件应在发送完成后安全清理。调整 memory_limit 只能基于峰值和并发容量评估。

上线验收

测试最小、平均和允许的最大附件,邮件可打开且 PDF 内容完整。并发发送时 Worker 内存应回落,失败重试不得重复生成无限临时文件或重复发送。

生产环境操作前的检查

处理“Magento 2 邮件附件导致内存耗尽”前,先记录 Magento 版本、部署模式、问题 Store View、复现时间和最近一次发布。所有 SQL 默认先执行 SELECT;任何写操作、目录清理或配置切换都应确认影响范围、保留备份并准备回滚。多节点环境要核对各节点代码版本、app/etc/env.php 配置摘要与流量分布。

php bin/magento --version
php bin/magento deploy:mode:show
php bin/magento maintenance:status
php bin/magento indexer:status
php bin/magento cache:status

命令应由 Magento 文件所有者在项目根目录执行。不要在生产高峰同时运行全量索引、静态部署和数据修复;涉及客户、支付、税费或订单的改动,先在脱敏数据副本验证。

建立可比较的排查记录

每次实验只改变一个变量,记录操作前值、命令、开始与结束时间、结果和回滚方式。准备一个正常对象与一个异常对象对照;若旧对象恢复而新对象仍能复现,说明根因尚未消除。

检查层需要保存的证据通过标准
入口层 URL、状态码、请求 ID、节点 路由和协议一致
应用层 异常堆栈、模块、作用域 无新异常且可重复
数据层 主键、时间、关联行数 关系完整且无重复副作用
业务层 正常、失败、重试路径 最终结果一致

修复后的观察窗口

上线后至少观察一轮 Cron、队列和索引周期,并在两台节点、无痕浏览器和目标 Store View 重复验证。临时调试日志必须脱敏,确认错误率、响应时间和数据量稳定后及时关闭。