news 2026/4/25 2:24:05

每天学一个算法--外部排序(External Sorting)

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
每天学一个算法--外部排序(External Sorting)

📘 教案 23:外部排序(External Sorting · 工程级)


一、问题模型(必须精确定义)

给定一个包含 (N) 条记录的数据集,单条记录大小为 ® 字节;主存(RAM)可用容量为 (M) 字节;磁盘以**块(block)**为单位读写,每块大小为 (B) 字节。

目标:对全部数据按某键排序。

约束:

[N⋅R≫M][ N \cdot R \gg M ][NRM]

即:数据无法一次性装入内存


二、评价指标:I/O 复杂度

外部排序的瓶颈不是 CPU,而是磁盘 I/O 次数。标准度量为:

[I/O cost=读块数+写块数][ \text{I/O cost} = \text{读块数} + \text{写块数} ][I/O cost=读块数+写块数]

我们以“扫描全数据一次”的 I/O 代价为基准:

[Θ!(NRB)][ \Theta!\left(\frac{N R}{B}\right) ][Θ!(BNR)]


三、两阶段多路归并(TPMMS)

外部排序的标准算法是Two-Phase Multiway Merge Sort (TPMMS)

Phase 1:生成初始有序段(runs)

  • 每次读入至多 (M) 字节(约 (M/R) 条记录)
  • 在内存中排序(任意内部排序算法)
  • 写回磁盘形成一个有序段(run)

得到有序段数量:

[r≈⌈NRM⌉][ r \approx \left\lceil \frac{N R}{M} \right\rceil ][rMNR]


Phase 2:多路归并(k-way merge)

  • 同时打开 (k) 个有序段
  • 为每个段分配一个输入缓冲区,为输出分配一个输出缓冲区
  • 使用最小堆(优先队列)维护当前各段的最小元素
  • 反复输出最小元素,直到完成

四、关键参数:归并路数 (k)

设每个缓冲区大小为 (B),需要:

  • (k) 个输入缓冲
  • 1 个输出缓冲

内存约束:

[(k+1)⋅B≤M⇒k≤⌊MB⌋−1][ (k + 1) \cdot B \le M \Rightarrow k \le \left\lfloor \frac{M}{B} \right\rfloor - 1 ][(k+1)BMkBM1]


五、归并轮数(passes)

若一次不能归并完(即 (r > k)),需多轮归并。

归并轮数约为:

[⌈log⁡kr⌉][ \left\lceil \log_k r \right\rceil ][logkr]


六、总 I/O 复杂度

  • Phase 1:读一遍 + 写一遍
    [2⋅Θ!(NRB)][ 2 \cdot \Theta!\left(\frac{N R}{B}\right) ][2Θ!(BNR)]

  • Phase 2:每一轮归并读+写一遍

总计:

[Θ!(NRB)⋅(1+⌈log⁡kr⌉)⋅2][ \Theta!\left(\frac{N R}{B}\right) \cdot \left(1 + \left\lceil \log_k r \right\rceil \right) \cdot 2 ][Θ!(BNR)(1+logkr)2]

简化表达(忽略常数):

[O!(NBlog⁡MBNM)][ O!\left(\frac{N}{B} \log_{\frac{M}{B}} \frac{N}{M}\right) ][O!(BNlogBMMN)]

这就是外部排序的经典 I/O 复杂度。


七、Phase 1 优化:替换选择(Replacement Selection)

目标:让初始有序段更长,减少 ®

方法:

  • 使用最小堆维护当前可输出元素
  • 新读入元素若 (\ge) 上一次输出,则继续当前 run
  • 否则标记为“下一 run”

结果:在随机输入下,平均 run 长度约为:

[≈2M][ \approx 2M ][2M]

于是:

[r≈NR2M][ r \approx \frac{N R}{2M} ][r2MNR]

👉 直接减少一半的段数,从而减少归并轮数。


八、归并实现细节(可落地)

1. 缓冲区设计

  • 每个输入 run 分配 1 个 block 缓冲
  • 输出 1 个 block 缓冲
  • I/O 采用顺序读写(避免随机 I/O)

2. 数据结构

  • 最小堆大小为 (k)
  • 每个堆元素记录:当前值 + 来源 run 的标识

3. 流程

  1. 各 run 读入首块到输入缓冲

  2. 将各缓冲的首元素入堆

  3. 反复:

    • 弹出最小值写入输出缓冲
    • 从对应 run 的缓冲取下一个元素入堆
    • 若该缓冲耗尽,则读入该 run 的下一块

九、工程优化要点

1. 增大 (k)

[k↑⇒归并轮数↓][ k \uparrow \Rightarrow \text{归并轮数} \downarrow ][k↑⇒归并轮数]

但受限于 (M/B)。


2. 顺序 I/O

  • 避免随机读写(性能数量级差异)
  • 文件按 run 连续存储

3. 压缩 / 列式存储(场景相关)

减少 ®,从而减少 I/O 量。


4. 并行化

  • 多磁盘并行读写

  • MapReduce / Spark:

    • Map:生成局部有序段
    • Shuffle:按 key 分区
    • Reduce:分区内归并

十、与内存排序的本质差异

维度内存排序外部排序
资源瓶颈CPUI/O
访问模式随机访问顺序访问优先
优化目标比较次数I/O 次数

十一、适用场景

  • TB / PB 级日志排序
  • 数据仓库(ETL 排序阶段)
  • 数据库 ORDER BY / GROUP BY
  • 大规模去重(结合外部排序 + 归并去重)

十二、结论性表述

外部排序通过将数据划分为可内存处理的有序段,并以多路归并的方式逐步合并,在受限内存条件下以最小化 I/O 次数为目标完成全局排序,其性能由块大小、内存容量与归并路数共同决定。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/4/25 2:21:28

使用LLaMA-Factory进行LoRA微调实战(一步步演示)-原理源码解析

1. 问题背景与分析目标 标题对应的技术问题: 在大规模预训练语言模型(如LLaMA)中进行LoRA微调是提升模型在特定任务中性能的关键步骤。LoRA(Low-Rank Adaptation)作为一种高效的微调技术,通过引入低秩矩阵的…

作者头像 李华
网站建设 2026/4/25 2:21:21

机器学习工程师必备:Docker容器化实战指南

1. 机器学习工程师的Docker实战指南作为一位在机器学习领域摸爬滚打多年的工程师,我深刻理解环境配置带来的痛苦。记得有一次,我花了整整三天时间只为让同事的电脑跑通我的模型——Python版本不匹配、CUDA驱动冲突、系统库缺失...这些噩梦般的经历促使我…

作者头像 李华
网站建设 2026/4/25 2:20:21

【限时开放|C23内存安全实验室原始数据包】:2026年对Linux 6.12、Zephyr 4.0、FreeRTOS 2026.03的137万行C代码扫描结果(含TOP5致命模式热力图)

更多请点击: https://intelliparadigm.com 第一章:C23内存安全实验室原始数据包全景解读 C23标准在内存安全方面引入了多项关键增强,其中原始数据包(Raw Packet)分析是验证新约束机制有效性的重要手段。实验室捕获的…

作者头像 李华
网站建设 2026/4/25 2:19:16

大语言模型评估指标全解析与应用实践

1. 大语言模型评估指标入门指南 在自然语言处理领域,大语言模型(LLM)的评估一直是个令人头疼的问题。不同于传统机器学习任务有明确的准确率、召回率等指标,LLM的评估需要考虑语言质量、连贯性、事实准确性、创造性等多个维度。我曾在三个不同的LLM项目中…

作者头像 李华
网站建设 2026/4/25 2:17:59

GD32E503RE实测:深度睡眠电流超标?手把手教你配置IO口降到手册值

GD32E503RE深度睡眠电流优化实战:从1.5mA到0.2mA的完整调优指南 当我在智能家居传感器项目中首次使用GD32E503RE时,遇到了一个令人头疼的问题——深度睡眠模式下电流始终维持在1.5mA左右,远高于数据手册标注的0.2mA典型值。这个看似微小的差异…

作者头像 李华
网站建设 2026/4/25 2:14:32

62% 数据增长背后:2026 企业自用商城系统源码行业点评推荐

电商系统源码行业 62% 数据增长背后:2026 企业自用商城系统源码行业点评推荐记者:吴艳佳 | 2026 年 4 月 24 日一、行业爆发:62% 增速背后的三大核心动因2026 年一季度,国内企业自用商城系统源码市场迎来爆发式增长,整…

作者头像 李华