У меня есть плоская база данных, основанная на 65536 файлах, каждый из которых содержит одно слово за строкой, начиная с двух шестнадцатеричных символов.

Они выглядят так:

afword
46word2
Feword3
...

Я делаю десятки тысяч запросов в день, поэтому я ищу лучший способ найти строку, начинающуюся с двух шестнадцатеричных символов. Файлы были отсортированы перед сжатием.

На данный момент я делаю:

LC=ALL zgrep --text '^af' file

Есть ли другой более быстрый способ сделать это в perl или bash или в любой командной строке?

3
Jeff Schaller 5 Сен 2021 в 17:30
1
ИТИМ LC_ALL=C zgrep --text '^af' file
 – 
Stéphane Chazelas
22 Авг 2021 в 19:33
Поиск строк отсортированных файлов, начинающихся с префикса, — это то, что делает утилита look. Но вот у вас есть один файл с большим количеством строк или несколько файлов с большим количеством строк, и вам нужно заглянуть во все из них? Если вам нужно распаковать много маленьких файлов, запустив один вызов zcat -f для каждого файла (как это делает скрипт zgrep), это будет неэффективно.
 – 
Stéphane Chazelas
22 Авг 2021 в 19:36
2
Похоже, вы можете захотеть поместить эти данные в базу данных. Распаковывать все данные десятки тысяч раз в день кажется немного чрезмерным и расточительным.
 – 
Kusalananda
22 Авг 2021 в 19:38
Вы говорите, что ваши файлы отсортированы, но образец, который вы показали, - нет. Или вы имеете в виду, что он отсортирован на основе содержимого строк, начинающихся с 3-го символа (игнорируя двухзначное шестнадцатеричное число)?
 – 
Stéphane Chazelas
22 Авг 2021 в 19:41
Спасибо за ваши ответы. У меня недостаточно места для хранения этого в обычной базе данных, поэтому я сделал свою собственную систему. Я знаю, в каком файле искать, не нужно искать в нескольких файлах. Будет ли zcat piped выглядеть быстрее? Я могу попробовать. На данный момент у меня есть 1,7 ТБ ssd db, это будет более 3,5 ТБ без сжатия, что трудно найти по хорошей цене. Файлы были отсортированы до gzip, не позаботьтесь о пробе, которую я поставил, извините.
 – 
James
22 Авг 2021 в 19:43

1 ответ

zgrep (тот, что поставляется с gzip) — это сценарий оболочки, который в конце делает что-то вроде zcat | grep. Один из zutils делает то же самое, за исключением того, что он написан на C++ и поддерживает больше форматов сжатия. Он по-прежнему вызывает gzip и grep в отдельных процессах, связанных каналом.

С таким простым поиском у grep гораздо более простая задача, чем у zcat, поэтому, если вы придерживаетесь того же подхода к организации своих данных, я бы посоветовал сосредоточиться на работе над улучшением аспектов сжатия.

Здесь, работая над файлом, сгенерированным с помощью xxd -p -c35 < /dev/urandom | head -n 760000 | sort, я обнаружил, что при сжатии gzip использование pigz -dc вместо zcat (он же gzip -dc) ускоряет работу в несколько раз. 2.

Сжимая его с помощью lz4 --best, я получаю файл на 30% больше, но время распаковки сокращается в 100 раз:

$ zstat +size a*(m-1)| sort -k2n | column -t
a.xz    26954744
a.lrz   26971363
a.bz2   27412562
a.gz    30353089
a.gz3   30727911
a.lzop  38000050
a.lz4   40261510
a       53960000
$ time lz4cat a.lz4 > /dev/null
lz4cat a.lz4 > /dev/null  0.06s user 0.01s system 98% cpu 0.064 total
$ time pigz -dc  a.gz > /dev/null
pigz -dc a.gz > /dev/null  0.36s user 0.02s system 126% cpu 0.298 total
$ time gzip -dc a.gz > /dev/null
gzip -dc a.gz > /dev/null  0.47s user 0.00s system 99% cpu 0.476 total
$ time lz4cat a.lz4 | LC_ALL=C grep '^af' > /dev/null
lz4cat a.lz4  0.07s user 0.02s system 60% cpu 0.142 total
LC_ALL=C grep '^af' > /dev/null  0.07s user 0.00s system 53% cpu 0.141 total
$ time pigz -dc a.gz | LC_ALL=C grep '^af' > /dev/null
pigz -dc a.gz  0.36s user 0.04s system 130% cpu 0.303 total
LC_ALL=C grep '^af' > /dev/null  0.06s user 0.01s system 23% cpu 0.302 total
$ time gzip -dc a.gz | LC_ALL=C grep '^af' > /dev/null
gzip -dc a.gz  0.51s user 0.00s system 99% cpu 0.513 total
LC_ALL=C grep '^af' > /dev/null  0.08s user 0.01s system 16% cpu 0.512 total

lzop --best ненамного отстает от lz4 и немного лучше сжимает мой образец.

$ time lzop -dc a.lzop | LC_ALL=C grep '^af' > /dev/null
lzop -dc a.lzop  0.24s user 0.01s system 85% cpu 0.293 total
LC_ALL=C grep '^af' > /dev/null  0.07s user 0.01s system 27% cpu 0.292 total
3
Stéphane Chazelas 23 Авг 2021 в 07:54
Спасибо за уделенное время. Я попробовал lz4, но мне удалось увеличить использование дискового пространства только на 50%, а не на 30%. Я должен сказать, что слова содержат все буквенно-цифровые символы, так что, может быть, они не сжимаются так хорошо, как только шестнадцатеричные буквы или около того? Я не знаю. Я пробовал lzop, я получаю на 40% больше использования диска и примерно на 35% меньше времени поиска. Также мое сжатие gzip немного лучше, чем gzip, потому что я использую pigz -11, что требует времени, но файлы обречены быть статическими. Я также пробовал gzip -dc, но это не помогло вовремя.
 – 
James
22 Авг 2021 в 22:13
1
@ Джеймс, ты пробовал pigz (параллельный gzip (де)компрессор)?
 – 
Stéphane Chazelas
22 Авг 2021 в 22:45
@СтивенКитт. Спасибо. Я добавил примечание о zutils. Я спрашивал о pigz, потому что, хотя он и распараллеливает распаковку, он все же быстрее, чем gzip для распаковки (он по-прежнему использует несколько потоков, как указано на странице руководства).
 – 
Stéphane Chazelas
23 Авг 2021 в 07:57
Правильно, использование pigz для сжатия не означает, что оно также используется для распаковки, поэтому стоит спросить.
 – 
Stephen Kitt
23 Авг 2021 в 07:59
1
@StephenKitt, извините за опечатку в моем комментарии выше. Я имел в виду, что он не распараллеливает распаковку (но все равно работает быстрее для меня и использует несколько потоков, отличных от распаковки).
 – 
Stéphane Chazelas
23 Авг 2021 в 08:07