应使用 openpyxl 的 write_only 模式或 xlsxwriter 导出百万行数据:前者内存恒定约50MB,后者更快更稳、内存≤200MB,均需流式逐行写入且不支持公式/图表。

直接用 pandas.to_excel() 导出百万行数据,基本会卡死或内存爆掉——这不是你的代码问题,而是默认写入模式根本没做流式控制。
为什么 openpyxl 默认写入撑不住百万行?
openpyxl 的普通 Workbook 模式会把整张表的单元格对象、样式、公式缓存全保留在内存里。100 万行 × 10 列 ≈ 千万个 Cell 实例,每个实例带坐标、值、样式引用等开销,轻松吃掉 2–4GB 内存,且写入时频繁触发 Python 对象分配和 GC 压力。
- 禁用
ws.sheet_view.showGridLines = False和wb.calculation_engine = None只能省 10–15% 内存,治标不治本 -
Worksheet.append()看似高效,但底层仍是逐行构建 Cell 对象,行数一过 50 万就明显变慢 - 任何带样式的写入(哪怕只是字体加粗)都会让内存占用翻倍
必须切换到 write_only 模式
openpyxl 提供了真正的流式写入路径:Workbook(write_only=True)。它不维护 Cell 对象树,只按行序列化写入 XML 片段,内存恒定在 ~50MB 左右,与数据量无关。
- 不能读取已写入的单元格,也不能操作已有 sheet —— 这是流式写入的代价
- 必须用
ws.append(row),且row必须是 list 或 tuple,不能是 dict 或 Series - 不能设置单个单元格样式,但支持整行/列样式(通过
ws.row_dimensions[1].font等),需在 append 前设置 - 示例关键片段:
wb = Workbook(write_only=True)<br>ws = wb.create_sheet()<br>ws.append(["ID", "Name", "Amount"])<br>for i in range(1_000_000):<br> ws.append([i, f"User{i}", round(i * 1.23, 2)])<br>wb.save("output.xlsx")
更稳更快的选择:xlsxwriter
如果你不需要读取 Excel、只要导出,xlsxwriter 是比 write_only openpyxl 更优的工业级选择。它原生设计为只写,无对象模型负担,支持压缩、自定义格式、多 sheet 并发写入,百万行通常在 8–15 秒内完成,内存稳定在 200MB 以内。
快速生成专业的 Python 脚本和应用代码。一键创建完整项目结构,支持CLI、API、爬虫、Bot、Django等多种项目类型,包含完整的项目结构、配置文件、依赖管理、测试、README和文档。
立即学习“Python免费学习笔记(深入)”;
- 不依赖 Excel 安装环境,纯 Python 实现,跨平台兼容性更好
- 支持
workbook.add_worksheet()+worksheet.write_row(),写入速度比 openpyxl write_only 高 20–40% - 可配合
pandas.DataFrame.to_excel(engine='xlsxwriter'),但注意:必须传chunksize手动分块,否则 pandas 仍会先加载全量 DataFrame - 真实建议写法:
import xlsxwriter<br>wb = xlsxwriter.Workbook("output.xlsx")<br>ws = wb.add_worksheet()<br>ws.write_row(0, 0, ["ID", "Name", "Amount"])<br>for i, row in enumerate(data_generator(), start=1):<br> ws.write_row(i, 0, row)<br>wb.close()其中data_generator()应 yield list 类型行,避免一次性构造百万行列表
别忽略数据源头的瓶颈
真正拖慢导出的,往往不是 Excel 写入本身,而是上游数据准备:比如从数据库 fetchall() 拿出百万行再转成 list;或者用 pandas.read_sql() 加载全量再 .to_dict('records')。这些操作早就在内存里炸过了。
- 用数据库游标迭代:
cursor.execute(sql); while True: rows = cursor.fetchmany(10000); if not rows: break; yield rows - 避免
df.values.tolist(),改用zip(*df.values.T)或直接df.itertuples(index=False, name=None) - 字符串列务必提前转
category或 encode 为 bytes,object 类型是内存黑洞 - 导出前删掉所有非必要列:
usecols要在读取阶段就生效,而不是导出前 drop
write_only 模式和 xlsxwriter 都不支持公式、图表、条件格式这些“高级功能”——如果业务真需要这些,说明你该 rethink 整个交付形式了:Excel 不是报表系统,百万行+复杂样式本身就是反模式。要么拆成多个 sheet(每 sheet ≤ 10 万行),要么导出为 CSV + 附带 Excel 模板供用户手动透视。


















