千万级数据排序需分场景:能全量入内存时用 heapq.nsmallest() 取 Top-K(O(n log k)、内存恒定),但不可替代全局排序;超内存则必须外部排序——分块排序后用 heapq.merge() 流式归并,控制分块大小、临时文件和缓冲策略。

直接用 sorted() 或 list.sort() 处理千万级以上数据,大概率触发 MemoryError 或让系统卡死——这不是代码写错了,而是设计上没绕过内存墙。关键在两点:数据是否能全量进内存?若不能,必须用外部排序;若能但想提速,heapq.nsmallest() 仅适用于“取 Top-K”,不是全局排序的替代方案。
用 heapq.nsmallest() 取前 K 个时别误当全局排序用
heapq.nsmallest(k, iterable) 的时间复杂度是 O(n log k),只维护大小为 k 的堆,内存占用恒定,适合“查最小的 100 个数”这类场景。但它不产生全局有序结果,也不能跳过中间数据做流式归并。
- 常见错误:拿它代替
sorted()对整个列表排序,结果只返回前 K 个,后序逻辑直接崩 - 适用条件:明确只需要头部少量元素(如分页第一页、异常值检测)
- 注意参数:当
k接近n(比如要取前 90%),性能反而比sorted()差,此时堆操作开销盖过了收益
数据超内存时,必须走外部排序流程
核心是“分块 → 内存排序 → 归并”,每步都得控制住内存峰值。临时文件路径、缓冲区大小、分块粒度这些参数稍不注意,就会把磁盘 IO 拖成瓶颈。
- 分块大小建议设为可用内存的 1/3~1/2(例如 16GB 内存,单块控制在 4–6GB 数据量内),避免 swap 频繁触发
- 写临时文件务必用二进制模式或指定
buffering参数,文本模式默认行缓冲可能放大内存压力 - 归并阶段优先用
heapq.merge()而非手动实现最小堆——它原生支持多迭代器,且返回生成器,不会一次性加载全部归并结果 - 临时文件记得用
tempfile.NamedTemporaryFile(delete=False)创建,并在归并后显式os.unlink(),否则磁盘悄悄被占满
用 heapq.merge() 合并已排序块时的陷阱
heapq.merge(*iterables) 要求每个输入迭代器本身已升序,且它返回的是惰性迭代器。直接 list(heapq.merge(...)) 会把全部结果拉进内存,前功尽弃。
立即学习“Python免费学习笔记(深入)”;
- 正确做法:边归并边写入输出文件,例如
for item in heapq.merge(*sorted_files): output.write(str(item) + '\n') - 如果输入来自文件句柄,确保每个句柄都已重置到开头(
f.seek(0)),否则读不到数据 - 各块排序结果类型需一致,混用字符串和整数会导致
TypeError: ' - Python 3.12+ 支持
key参数,但旧版本不支持,跨版本部署时得自己预处理字段对齐
外部排序真正的复杂点不在算法,而在 IO 路径的稳定性:临时目录权限、磁盘空间余量、文件编码一致性、中断恢复能力——这些细节漏掉一个,跑一半失败就只能从头再来。


















