write_only=True模式是解决openpyxl Web导出内存溢出最直接有效的手段,它逐行写入XML流、不构建内存对象树,100万行数据内存通常稳定在32MB以下,但需注意单单元格内容不能超过32767字符。

openpyxl 的 write_only=True 模式是解决 Web 导出 Excel 内存溢出最直接有效的手段——它不构建内存中的工作表对象树,而是逐行写入 XML 流,100 万行数据内存通常稳定在 为什么普通 openpyxl 导出会 OOM
默认模式下,Workbook() 会把整张表的单元格、样式、公式、合并区域等全部加载进内存并维护对象引用。一个含样式的 50 万行 Excel,在内存中可能膨胀到 2GB+,Web 进程(尤其是 gunicorn/uWSGI 多 worker 场景)极易被系统 kill。
常见错误现象包括:
-
MemoryError抛出,堆栈末尾常带openpyxl.workbook.workbook.Workbook或openpyxl.cell.cell.Cell - 导出接口响应超时,Nginx 返回
504 Gateway Timeout(实际是后端卡死或被 OOM killer 干掉) - 同一台机器并发导出 2–3 个大文件,系统内存瞬间吃满
write_only 模式怎么用才不踩坑
它不是“打开开关就能用”,有几个硬性约束必须遵守:
- 必须在初始化
Workbook时传write_only=True,之后无法切换 - 只能调用
ws.append()写行,不能读、不能改已写入的单元格(ws['A1'] = ...会报ValueError: Cannot access cells in a write only workbook) - 不支持公式计算(但可写入公式字符串,如
"=SUM(A2:A100)",Excel 打开后自动计算) - 图片只能插入到工作表级(
ws._images.append(...)),不能锚定到某单元格;超链接可用cell.hyperlink设置 - 字体、边框、填充等样式**全部不生效**——这是最大妥协点,如需样式,得用模板 + 替换数据,或改用
xlsxwriter分块写入
数据库分页 + write_only 流式导出实战
Web 导出本质是「拉取 → 转换 → 写入」三阶段,任一环节全量加载都会崩。关键是要让数据源也流起来:
立即学习“Python免费学习笔记(深入)”;
- 用
cursor.arraysize = 5000+fetchmany()(Oracle/MySQL)或engine.execution_options(stream_results=True)(SQLAlchemy)控制单次拉取量 - 不要把所有数据先
list(cursor.fetchall()),而要用生成器封装:def data_generator(): while True: rows = cursor.fetchmany(5000) if not rows: break yield from rows - 写入时直接迭代生成器:
for row in data_generator(): ws.append(row),避免中间 list 存储 - 务必在
wb.save(...)后显式调用wb.close()(虽然 save 会 flush,但 close 更稳妥)
容易被忽略的细节
很多人以为开了 write_only 就万事大吉,但以下三点常导致隐性失败:
-
datetime对象写入时若未转为str或date,openpyxl会尝试序列化,触发大量临时对象创建,内存反而上涨 - 字段里混有
None、float('nan')、嵌套dict等非标类型,ws.append()会静默转成字符串,但某些情况下引发编码异常或格式错乱 - 导出前没做字符长度校验,超长文本(如日志字段 > 32767 字符)会被截断,且无警告——Excel 规范限制单单元格最多 32767 字符


















