最快的方式读取巨型文件中的单行

时间:2015-03-08 12:53:15

标签: file sed line

所以我有一个网站,我需要在一个巨大的文本文件(~2GB)中访问一行(行号已知)。

我得出结论

  

system_exec(“sed -n 3p<< /file/whatever.txt”);

PHP中的

是最有效的方式。

但是我觉得使用它感觉不太舒服,这似乎是一个糟糕的黑客和不安全感。使用它真的没问题吗?如果没有PHP框架,这种方式是否可行?或者有更有效的方法来做到这一点吗?

3 个答案:

答案 0 :(得分:2)

在巨型文件中打印单行的最快方法是使用q(退出)命令

sed -n '3{p;q}' yourFile

这将打印第3行,然后sed将停止工作。

答案 1 :(得分:1)

以下是您可以将各种方法转移到文件中的方法,以及一些粗略的基准测试。

我创建了一个包含90M行的文本文件。每行包含一些#####'尽管这些数字与实际行不匹配(以便更快地创建样本数据)。

$ wc bigfile.txt
90000000 90000000 1340001000 bigfile.txt

$ ls -lrth bigfile.txt
-rw-rw-r--  1 admin  wheel   1.2G Mar  8 09:37 bigfile.txt

这些基准测试是在运行OS 10.10.2的1.3GHz i5,4GB RAM,MacBook Air(11英寸,2013年中)上进行的。

首先,是awk。我真的期待更好。

$ time awk 'NR == 10000000{print;exit}' bigfile.txt
something99999

real    0m12.716s
user    0m12.529s
sys     0m0.117s

tail表现稍好,但仍然很慢。

$ time tail -n +10000000 bigfile.txt | head -n 1
something99999

real    0m10.393s
user    0m10.311s
sys     0m0.066s

正如您所知,由于某种原因,sed方式迄今为止优于其他竞争者。但是,仍然慢得令人无法接受。

$ time sed -n '10000000{p;q;}' bigfile.txt
something99999

real    0m3.846s
user    0m3.772s
sys     0m0.053s

如果您有常规数据(每行相同的字节数或可以确定地计算每行的字节数),您可以放弃完全读取文件并直接偏移到文件中。这是最快的选择,但在数据格式方面也是最严格的选择。这就是William Pursell在建议将数据填充到固定大小时所得到的。

$ time tail -c +10000000 bigfile.txt | head -n 1
thing71851

real    0m0.020s
user    0m0.011s
sys     0m0.006s

但是,如果您有2G文本文件,则应考虑使用正确的数据库。

$ time sqlite3 bigfile.db << EOF
> create table bigdb(data text);
> .import bigfile.txt bigdb
> EOF

real    3m16.650s
user    3m3.703s
sys     0m4.221s

$ ls -lrth bigfile.db
-rw-r--r--  1 admin  wheel   1.9G Mar  8 10:16 bigfile.db

既然你有一个数据库,你应该能够获得超快的速度吗?只有你正确使用它。 OFFSETLIMIT的第一个参数)因为速度太慢而臭名昭着,应该避免。

$ time sqlite3 bigfile.db <<< 'select * from bigdb limit 10000000-1, 1;'
something99999

real    0m2.156s
user    0m0.688s
sys     0m0.440s

您应该拥有正确的主键,或使用sqlite方便的内部列ROWID来获得最佳效果。

$ time sqlite3 bigfile.db <<< 'select * from bigdb where ROWID == 10000000;'
something99999

real    0m0.017s
user    0m0.003s
sys     0m0.005s

答案 2 :(得分:0)

在我的系统上,我得出了完全不同的结论 Environnement:KSH下的AIX

FileName=listOfBig.txt
# ls -l -> 239.070.208 bytes
# wc -l listOfBig.txt | read FileSize Ignore
FileSize=638976

# take a portion of 8 lines at 1000 lines of the end
LineToStart=$(( ${FileSize} - 1024 ))
LineToTake=8
LineToStop=$(( ${LineToStart} + ${LineToTake} - 1 ))

time sed -n "${LineToStart},${LineToStop} p;${LineToStop} q" ${FileName} >/dev/null
real    0m1.49s
user    0m0.45s
sys     0m0.41s

time sed "${LineToStart},${LineToStop} !d;${LineToStop} q" ${FileName} >/dev/null
real    0m1.51s
user    0m0.45s
sys     0m0.42s

time tail -n +${LineToStart} ${FileName} | head -${LineToTake} >/dev/null
real    0m0.34s
user    0m0.00s
sys     0m0.00s

time head -${LineToStop}  ${FileName} | tail -${LineToTake} >/dev/null
real    0m0.84s
user    0m0.75s
sys     0m0.23s

第二次和后续测试肯定有一个小优势(第一次(缓存,...)但不是很不一样

所以,在这个测试中,sed要慢得多(不是像Linux上那样的GNU版工具)。

如果文件正在发生变化(通常是日志中的情况),还有另一个问题是在巨大文件的情况下无法解释(可能发生在很小但很少发生)是管道流的问题。我曾经遇到过这个问题,应该创建一个临时文件(也非常大),以便处理该行的其他请求(如果有的话)。