现有一个很大的文件(比如 40G 的文本),假如我随机定位某一行,怎么样实现最快(时间和空间)?
希望 V 友们想什么说什么,畅所欲言
希望 V 友们想什么说什么,畅所欲言
|
ashfinalAug 22, 2017
1
40 G …… 这,这
|
|
15015613Aug 22, 2017 via Android
2
查找内容的话,grep
输出特定行,sed |
|
3
@15015613 grep 是顺序查找太慢,sed 也需要遍历
|
|
FishdrownedAug 22, 2017 via Android
4
要是打开没规律的文本文件,你倒是说什么算法不用遍历?判断行数难道不用先知道前面有几行?
|
|
5
|
|
FishdrownedAug 22, 2017 via Android
6
优化方法估计只能多核并行遍历了。如果文件内容不会变化,遍历完之后可以缓存结果
|
|
chunkAug 22, 2017
7
dd
|
|
sofsAug 22, 2017 via Android
8
40G 的文本我不敢去操作
|
|
qian19876025Aug 22, 2017
9
@Fishdrowned 除非大部分读进内存 不然你没法 并行
|
|
sun1991Aug 22, 2017
10
先扫描一遍, 获取所有行的 pos, 保存起来.
|
|
reusAug 22, 2017
11
加索引
|
|
FishdrownedAug 22, 2017 via Android
12
@qian19876025 当然可以并行。按文件大小设置几个断点,然后每个线程从断点处开始遍历,找到换行符就记录一下位置,并不需要多少内存。全部完成后汇总,每一行的位置都出来了
|
|
FanWallAug 22, 2017 via Android
13
@Fishdrowned 最坏的情况缓存大小是文件大小的四倍
|
|
FishdrownedAug 22, 2017 via Android
14
缓存大不用怕,因为缓存是有组织的,内存不够就放磁盘,快速查找不难实现。
|
|
0ZXYDDu796nVCFxqAug 22, 2017 via iPhone
15
只查找少数几次:调用 grep,如果你实现了一个比 grep 更快的算法,那牛逼大了
经常查找:扫一遍,建个索引存下来 |
|
lululauAug 22, 2017
16
扫描一遍文件,把每一个换行符的索引记录下来,然后 dd 就可以了,不知道 dd 就 man 2 lseek
|
|
websterAug 22, 2017
17
用 grep 找过 30G 的文件 感觉真的很科技 很快
|
|
privilAug 22, 2017
18
|
|
gouchaoerAug 22, 2017 via Android
19
ls 的回答都是什么啊,lz 需求是定位某一行
fseek 可以访问文件偏移量,但是行是以换行符确定的,你需要遍历整个文件 要以常数时间定位某一行的话自己建立一个索引就 ok 了,先遍历文件遇到换行符就记录这是第几行,以及当前 fseek 偏移量 |
|
20
@gstqc 建索引需要时间,存索引需要的空间最后貌似比源文件都大了把
|
|
EchoUtopiaAug 22, 2017 via iPhone
21
awk
|
|
rrfengAug 22, 2017
22
随机定位是什么鬼?
你随机给个数字,我输出行号等于这个数字的行? |
|
0ZXYDDu796nVCFxqAug 22, 2017 via iPhone
23
@kaiser1992 索引表怎么可能比原文件大
你要建立的是行数和偏移量两个值 |
|
ynyounuoAug 22, 2017
24
Ctags
不过 40G ???这是什么文本啊 |
|
am241Aug 22, 2017 via Android
25
扫一遍,记录下每个换行符的位置,然后二分搜索就够了
|
|
qian19876025Aug 23, 2017
26
@Fishdrowned 那你还是要把东西大部分读进内存啊 难不成你不用? 难不成你知道硬盘中怎么存储的? 不读进内存你来搞
|
|
qian19876025Aug 23, 2017
27
当然如果你的数据是 线性排序 或者 hash 固定 没有冲突的 那是另外的话了
|
|
qian19876025Aug 23, 2017
28
如果每行数据是 固定大小的 或者能直接算出偏移值的 那也可以直接取出
|
|
watzdsAug 23, 2017 via Android
29
顺序读取文件似乎每秒几百兆,30g 遍历大概得几分钟吧。
还有个问题,30g 文件中间加一行会怎样?😁 什么需求需要单机存储 30g 大文件?可能一开始就不应该这么存 |
|
FanWallAug 23, 2017 via Android
30
@gstqc #23 都不用想最坏情况了,假设一个字节一行,换行符是\n,40G 光记录偏移值就要 160G 了(UL 是装不下的),如果再考虑全部空行…… 320G ?
@qian19876025 #26 我觉得他是对的,索引是有组织的,是可以计算的,只读需要的一小段就行了。 |
|
FishdrownedAug 23, 2017 via Android
31
@qian19876025 #26 大哥,我不知道你是怎么理解的,遍历的时候,读一小段文字进内存,获取到换行位置之后就可以释放了然后读下一段了啊,难道你要一直放在内存里?
|
|
FishdrownedAug 23, 2017 via Android
32
打个比方,楼主有一本一万页的书,想随机精确地定位到第几个段落。
我一个人数太慢,于是叫来 20 个人(多线 /进程),每人撕走 2000 页,让他们各自统计自己的 2000 页里面各有多少段。 然后我等这 20 个人数完了,汇总整理一下做个索引,我不就知道这本书有几段了? 每个线程做的事情没有什么花巧,就是遍历,只不过是适合并行计算,把时间分摊了。 至于内存,每个人都不用把 2000 页背下来啊,他只要知道每个段落位置分布在哪里就可以了,内存不够就拿笔写下来。 |
|
t6attackAug 23, 2017
33
“随机定位某一行” ,, 究竟是 “随机定位一行”?还是 “定位某一行”?
前者其实简单多了,用 64 位文件指针,随便定位个位置,向前、向后找到换行符就是了。感觉实际应用中,需要的应该就是前者。 |
|
t6attackAug 23, 2017
34
也不对。这样结果并不随机。内容多的行更容易被定位到。
|
|
ufjfengAug 23, 2017 via iPhone
35
知道行号的话,awk 就行,性能不详
awk 'NR=n {print $0}' n 是行号 |
|
ufjfengAug 23, 2017 via iPhone
36
awk 'NR=n {print $0}' filename
楼上忘了写文件名 |
|
linux40Aug 23, 2017 via Android
37
40g 为什么是一个文件呢。。。
|
|
libookAug 23, 2017
38
定位某一行就是定位某一个换行符,和定位某一个字母 a 或定位某一个数字 1 是一样的,都需要遍历整个文件,除非是像数据库一样做索引优化。
个人以为 linux 提供的指令效率极高,如果还是满足不了需求的话建议想办法建起换行符的索引。 |
|
wangyucnAug 23, 2017
39
|
|
wangyucnAug 23, 2017
40
索引例子:
行数(单位千) 文件偏移 1 456789 (第 1000 行的文件偏移) 2 1234567 (第 2000 行的文件偏移) 3 2345678 (第 2000 行的文件偏移) 4 |
|
wangyucnAug 23, 2017
41
囧,正在编辑不小心发出去了。凑合看吧。 索引做成外挂式的,不需要改原文件。
|
|
90safeAug 23, 2017
42
grep 是个黑科技
|
|
huangfsAug 23, 2017
43
rg
|
|
44
@wangyucn 谢谢,前期构建索引需要遍历字符位置,感觉太耗时间了
|
|
45
@lululau 换行符都是\n 或者\r\n 相同符号如何构建索引?
|
|
46
@lululau 我明白你意思了,你是要对每一行换行符的位置做一个索引
|
|
HozzzAug 23, 2017
47
还不如将整个文件先拆成多个小文件,然后再并行查询(如果是构建索引,遇到频繁更新的文件,效率仍然不高)
|
|
48
@Hozzz 不考虑并行的方式,拆分重写小文件,需要的时间也不少,还没有考虑之后查询的时间。
|
|
mhycyAug 23, 2017
49
楼上有人提到并行,但实际应用中可行性不大,因为数据是从磁盘顺序读取的。
构建索引只需要记录所有\r\n or \n 的偏移量,构建时间约等于读取 40G 文件所需的时间。 (如果 CPU 足够快的话) 如果 CPU 在判断字符过程中消耗过大(基本不可能,除非 IO 太快) 那么可考虑缓冲区读取多线程并行分析,但考虑到多线程开销,似乎还是单线程更靠谱。 注意:因为 IO 的限制,没有比这个更快的定位不定长数据行的办法。 |
|
xou130Aug 23, 2017 via Android
50
40G 这个大小该上 hadoop 了
|
|
qian19876025Aug 23, 2017
51
@Fishdrowned 不管你怎么样你翻书还是串行的 除非读进内存你这就是延迟而已
|
|
luoqengAug 23, 2017
52
|
|
vjnjcAug 23, 2017
53
借楼问一下中文件你们都是怎么操作的?
有一个 sql 文件,大概 100MB,我在 mac 下用 vim 打开都花了一分钟了。。。 |
|
HozzzAug 23, 2017
54
@kaiser1992 可以在存入文件之前就对数据进行拆分,或者用个额外的进程不停对该文件进行拆分
|
|
HozzzAug 23, 2017
55
@kaiser1992 另外 head 和 tail 命令不会遍历整个文件
|
|
FishdrownedAug 23, 2017 via Android
56
@qian19876025 #51 麻烦你了解一下磁盘寻址,我已经知道某行的偏移量在哪里了,找个 offset 还要串行?
|
|
FishdrownedAug 23, 2017 via Android
57
@kaiser1992 #44 这个时间(遍历整个文件)是必须付出的,没有捷径。
|
|
realpgAug 23, 2017
58
|
|
v2orzAug 23, 2017
59
6 楼+16 楼应该已经可以解决问题了
|
|
popokAug 23, 2017
60
传说中的社工库吗
|
|
shylingAug 23, 2017
61
2333,先把它分成多个文件啊。。。例如 n 行一个文件。。。
|
|
sola97Aug 23, 2017 via Android
62
社工库的既视感
|
|
FishdrownedAug 23, 2017 via Android
63
其实并行只是理论上的优化方法,实际上 cpu 处理的速度应该是高于磁盘的吞吐量的(几百 MB/s,不考虑随机读的 iops 是因为这个可以规避)
|
|
wangyucnAug 23, 2017
64
@kaiser1992 `不建索引`等于`每次查询都要浪费一次建索引的时间`
想想如果你的要定位的行在文件靠后的位置,因为每行长度不固定,没索引势必要遍历整个文件。 我看不出不建索引有什么优势。也许你的意思是你这个查询只需要做一次?如果只做一次就直接遍历吧。 |
|
sampengAug 23, 2017
67
从应用场景来说,如果只是达成这个目的。。我可能直接先把文件分成 n 个小文件。比如 1G 一个。。然后再算 seek 的目标在哪个文件里面。
只是做而做,感觉没有啥意义。。再快也没什么卵用 |
|
billleeAug 23, 2017
69
@Fishdrowned #6 如果是一块硬盘,并发只会更慢
|
|
SlipStupigAug 23, 2017
70
elastic search
|
|
rogerchenAug 23, 2017 via Android
71
kv 数据库不就是为楼主而生。
|
|
michael2016Aug 23, 2017
72
楼主,抬出我的意大利炮。。。
|
|
lybtongjiAug 23, 2017
73
倍增法
|
|
ashfinalAug 23, 2017
74
|
|
chunkAug 23, 2017
75
很多人关注点都在建索引上,但建索引的算法很依赖于数据的 pattern(比如如果是日志文件,每行的长度差不多)
|
|
elvodnAug 23, 2017
76
让给文件那边直接给原生 int 数组二进制文件方便
|
|
77
比 grep 快的当然有
the silver search, ag ripgrep, rg ripgrep 是 rust 写的,vsc 最近版本也集成了,蛮火 |
|
78
@ashfinal 搜索不耗时??
|
|
ashfinalAug 29, 2017
79
|
|
80
@ashfinal 你的 MacVim 读取文件是直接都读到内存了吧,你看看能否打开上 10G 的文件?
|
|
ashfinalAug 30, 2017
81
@kaiser1992 嗯 Vim 是默认读到内存的。不过我平时 200 MB 以上的文件都遇不到两个。
|