UNIX sort命令如何对很大的文件进行排序?


104

UNIX sort命令可以对非常大的文件进行排序,如下所示:

sort large_file

排序算法如何实现?

它怎么不会导致过多的内存消耗?


这是有趣的。我真的不知道它是如何工作的,但是我有一个猜测。可能会将每个键的第一个字符放入二叉树中,并且在发生冲突时,它还会使用键的下一个字符,因此它不会保存过多的键。然后,它可以使用每个键将偏移量保存到文件中,以便可以查找并按顺序打印每一行。
Zifre

实际上,@ ayaz如果您不是在磁盘上而是在管道中对文件进行排序,会更有趣,因为它很明显您不能简单地对输入数据进行多次传递。
tvanfosson,2009年

3
为什么SO上的每个人都总是这么猜测呢?

您可以对输入进行多次传递-您只需要读取所有输入,将其写入磁盘,然后对磁盘文件进行排序即可。

2
@Neil-从上下文看来,他似乎在试图对文件的内容而不是文件名(对于一个名称没有意义)进行排序。我只是想改善问题,而又不会过多地更改上下文,这样,由于一个简单的错误,它将得到答案而不是否决票。
tvanfosson,2009年

Answers:


111

UNIX Sort命令算法细节说,Unix Sort使用外部R-Way合并排序算法。该链接会涉及更多细节,但从本质上讲,它会将输入分成较小的部分(适合内存),然后最后将每个部分合并在一起。



12

警告:此脚本每块启动一个shell,对于非常大的文件,可能是数百个。


这是我为此目的编写的脚本。在4处理器的机器上,它将排序性能提高了100%!

#! /bin/ksh

MAX_LINES_PER_CHUNK=1000000
ORIGINAL_FILE=$1
SORTED_FILE=$2
CHUNK_FILE_PREFIX=$ORIGINAL_FILE.split.
SORTED_CHUNK_FILES=$CHUNK_FILE_PREFIX*.sorted

usage ()
{
     echo Parallel sort
     echo usage: psort file1 file2
     echo Sorts text file file1 and stores the output in file2
     echo Note: file1 will be split in chunks up to $MAX_LINES_PER_CHUNK lines
     echo  and each chunk will be sorted in parallel
}

# test if we have two arguments on the command line
if [ $# != 2 ]
then
    usage
    exit
fi

#Cleanup any lefover files
rm -f $SORTED_CHUNK_FILES > /dev/null
rm -f $CHUNK_FILE_PREFIX* > /dev/null
rm -f $SORTED_FILE

#Splitting $ORIGINAL_FILE into chunks ...
split -l $MAX_LINES_PER_CHUNK $ORIGINAL_FILE $CHUNK_FILE_PREFIX

for file in $CHUNK_FILE_PREFIX*
do
    sort $file > $file.sorted &
done
wait

#Merging chunks to $SORTED_FILE ...
sort -m $SORTED_CHUNK_FILES > $SORTED_FILE

#Cleanup any lefover files
rm -f $SORTED_CHUNK_FILES > /dev/null
rm -f $CHUNK_FILE_PREFIX* > /dev/null

另请参阅:“ 使用Shell脚本更快地对大型文件进行排序


35
您可以从GNU排序版本8.11开始使用sort --parallel N
jhclark 2011年

5
GNU coreutils 8.6实际上
bdeonovic

1
这是我的把戏。我有排序8.4版本。直接在文件上使用排序(1.9亿行)毫无用处。该程序仅用了不到4分钟的时间就做到了
Sunil B

再次,此答案与问题无关
WattsInABox

2
该脚本很危险。我的Linux机器在启动数百种排序过程后失去了响应……
Wu


11
#!/bin/bash

usage ()
{
    echo Parallel sort
    echo usage: psort file1 file2
    echo Sorts text file file1 and stores the output in file2
}

# test if we have two arguments on the command line
if [ $# != 2 ]
then
    usage
    exit
fi

pv $1 | parallel --pipe --files sort -S512M | parallel -Xj1 sort -S1024M -m {} ';' rm {} > $2

太好了 不知道有一个平行包装!使用上述方法后,排序时间提高了50%以上。谢谢。
xbsd

我尝试使用comm对此文件生成的文件进行比较,并警告我文件未排序。
ashishb 2014年

7

仔细查看排序选项以提高性能,并了解它对您的计算机和问题的影响。Ubuntu上的关键参数是

  • 临时文件的位置-T directory_name
  • 使用的内存量-SN%(使用的所有内存的N%越多越好,但要避免过度预订,否则会导致交换到磁盘。您可以像“ -S 80%”那样使用它来使用80%的可用RAM,或“ -S 2G”获得2 GB RAM。)

发问者问“为什么没有高内存使用率?” 答案来自历史,较旧的UNIX机器很小,并且默认内存大小设置为很小。为您的工作负载调整尽可能大的值,以大大提高排序性能。将工作目录设置为最快的设备上具有足够空间来容纳至少1.25 *大小的文件的位置。


在2.5GB的文件上,在具有64GB的RAM和-S 80%的盒子上尝试此操作,即使整个文件小于该文件,它实际上也使用了该百分比。这是为什么?即使它不使用似乎无用的就地排序
Joseph Garvin

在读取文件内容之前,sort -S可能为排序过程预先分配了内存。
弗雷德·甘内特

-3

内存应该不是问题-排序已经解决了。如果您想最佳利用多核CPU,我已经在一个小的脚本中实现了这一点(类似于您可能在网上找到的脚本,但是比大多数脚本更简单/更干净;)。

#!/bin/bash
# Usage: psort filename <chunksize> <threads>
# In this example a the file largefile is split into chunks of 20 MB.
# The part are sorted in 4 simultaneous threads before getting merged.
# 
# psort largefile.txt 20m 4    
#
# by h.p.
split -b $2 $1 $1.part
suffix=sorttemp.`date +%s`
nthreads=$3
i=0
for fname in `ls *$1.part*`
do
    let i++
    sort $fname > $fname.$suffix &
    mres=$(($i % $nthreads))
    test "$mres" -eq 0 && wait
done
wait
sort -m *.$suffix 
rm $1.part*

4
有趣的脚本,但是它不能回答这个问题。
约阿希姆·绍尔

5
split -b将按字节分割,从而在任意位置截断行
ithkuil 2012年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.