Ask HN: Sorting massive text files?
I've got a ~35GB text file full of data, and I want to parse it so I only have unique results in the end file. In the past, I've had no problem with cat FILE | sort | uniq, but I've never worked with anything of this magnitude. Even running a line count takes an extraordinary amount of time:
time cat FILE | wc -l 2608560847
real 11m18.148s user 1m35.667s sys 1m33.820s [root@server src]#
Any suggestions on how I can go about getting unique records from this type of file?
74 comments
[ 6.9 ms ] story [ 156 ms ] threadthen if you have multiple cores on your machine, you can run multiple instantiations of the same script on those smaller files and aggregate the results later.
if you're more ambitious, you could look into using a lightweight MapReduce framework
Maybe a kind of divide and conquer could work? Split into several files, do the sort | uniq on each of them. Then merge them, checking for duplicates on the way. I think merging should be almost as fast as line counting, at least linear in the size of the two files.
Edit: I guess it would be slower than counting, because presumably it would write to a new file (the merged file). But still, it should be linear.
It may also help to use compression with the temporary files (something like "--compress-program lzop"), but I've never tried that.
Did you really think sort(1) just bails if you pipe it more data than can be contained in memory?
You could use the same approach to simply take the unique tokens of the output of the final "count".
May be overkill if you you can read the file in less than an hour, but this approach (divide and conquer) may be a good inspiration.
Your 'cat' is not needed by the way, sort takes a filename as argument. And sort can '-u'!
It would be interesting if sort would fail on this (why?) or how long it would take.
edit: Why on earth are you doing this as root?
http://en.wikipedia.org/wiki/Bloom_filter
That sounds at least halfway feasible to me - you assume 1/3 duplicates, and 20 bytes per line, you'd get ~8GB worth of hashtable entries (i.e., if you want the hashtable 70% filled to limit the amount of collisions, you'd need 12GB of virtual memory to back the hashtable, but only rarely access it since you're using the Bloom filter).
(To the person who downvoted it: can you say why you don't like the idea?)
I know for a fact that they do do it on smaller batches, though. It takes a lot of room, as you can imagine!
Of course, if you want to learn hadoop then go for it. But it's probably more practical just to let it run :-)
http://vkundeti.blogspot.com/2008/03/tech-algorithmic-detail...
Thus, sort -u <filename> is your go-to for simple jobs. (Note that you'll need to have enough extra disk space to hold all of the temporary files.)
If you need to do something more sophisticated (e.g. joining lines from a web server log into sessions, then sorting sessions), you can still use divide-and-conquer, but you have to be smarter. Divide the file into N parts based on some logic (i.e. divide into files based on session ID), then sort the lines in each individually, then merge the results back together.
This is what map/reduce frameworks are made to do, of course, but something like Hadoop may be overkill unless you plan to do this type of thing often.
Here's a blog post I wrote about it:
http://arnab.org/blog/quick-and-easy-multicore-sort
Also, what do you want to do with the unique records? That might effect what initial processing method is best for your goal.
The only thing is that you have to split it up and merge after sorting (for which unix sort was ok enough).
Not sure why I got that result, but even with increased buffer size for unix sort it didnt much differ. I also didn't run the splitted sorts in parallel, which would of course have been a good idea.
You may have had a data set that tickled something that plays to Timsort's advantage; Timsort was basically designed to encounter that case as often as possible on real data.
Both can be rewritten as "sort FILE | uniq" and "time wc -l FILE".
And given the data size, both non-cat uses would likely be faster as well.
If that's what you're after, you don't need to sort at all, you can pick out your uniques with a 3 line script in your favourite language, split up the file first, if necessary.
I'd guess it's a gigantic spam list.
...and this is a problem most databases have addressed.
http://en.wikipedia.org/wiki/K_(programming_language)
The above can be written: awk -F(dataseperator) "{print $(number of seperation to print)}" FILE | sort -g | uniq | sort -g
http://en.wikipedia.org/wiki/External_sorting
The reason to split is not to replicate mergesort, but rather to get around single threading in GNU sort. Of course, there are better options if this is a regular task, but GNU sort just happens to be really common and easy if it's a one-off.
I do wish I had a better split (since split seems to read the whole file) and a better sort (that was parallel and reasonably common) though.