3

У меня есть несколько больших (как в: больше, чем любой словарь, 100 ГБ) файлов. Эти файлы имеют очень высокую энтропию и очень плохо сжимаются. Однако эти файлы (насколько я могу судить) почти полностью идентичны. (И не на самом деле сжатый)

В качестве тестового примера попробовал маломасштабное моделирование:

dd if=/dev/urandom of=random count=1G

cat random random random > 3random

gz -1 < 3random > 3random.gz
xz -1 < 3random > 3random.xz

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

Есть ли разумный способ сжать эти файлы? Это только для (автономного) архива, распаковка не будет выполняться часто.

2 ответа2

6

Давайте начнем с файла 10 МБ псевдослучайных данных и сделаем две его копии:

$ dd if=/dev/urandom of=f1 bs=1M count=10
$ cp f1 f2
$ cp f1 f3

Давайте изменим эти копии так, чтобы они были "почти полностью идентичны" (как вы сказали):

$   # Avoid typos and improve readability
$ alias random='od -t u4 -N 4 /dev/urandom |
  sed -n "1{s/^\S*\s//;s/\s/${fill}/g;p}"'
$ alias randomize='dd if=/dev/urandom bs=1 seek="$(
    echo "scale=0;$(random)$(random)$(random)$(random) % (1024*1024*10)" | bc -l
  )" count="$( echo "scale=0;$(random)$(random) % 512 + 1" |
    bc -l )" conv=notrunc'
$   # In files "f2" and "f3, replace 1 to 512Bytes of data with other
$   #+ pseudo-random data in a pseudo-random position. Do this 3
$   #+ times for each file
$ randomize of=f2
$ randomize of=f2
$ randomize of=f2
$ randomize of=f3
$ randomize of=f3
$ randomize of=f3

Теперь мы можем сжать данные в каждом файле, чтобы увидеть, что происходит:

$ xz -1 < f1 > f1.xz
$ xz -1 < f2 > f2.xz
$ xz -1 < f3 > f3.xz
$ ls -lh f{1..3}{,.xz}
-rw-rw-r-- 1 myuser mygroup 10M may 29 09:31 f1
-rw-rw-r-- 1 myuser mygroup 11M may 29 10:07 f1.xz
-rw-rw-r-- 1 myuser mygroup 10M may 29 10:00 f2
-rw-rw-r-- 1 myuser mygroup 11M may 29 10:07 f2.xz
-rw-rw-r-- 1 myuser mygroup 10M may 29 10:05 f3
-rw-rw-r-- 1 myuser mygroup 11M may 29 10:07 f3.xz

Мы видим, что это на самом деле увеличивает размер данных. Теперь давайте преобразуем данные в шестнадцатеричные удобочитаемые данные (ну, вроде как) и сжимаем результат:

$ xxd f1 | tee f1.hex | xz -1 > f1.hex.xz
$ xxd f2 | tee f2.hex | xz -1 > f2.hex.xz
$ xxd f3 | tee f3.hex | xz -1 > f3.hex.xz
$ ls -lh f{1..3}.hex*
-rw-rw-r-- 1 myuser mygroup 42M may 29 10:03 f1.hex
-rw-rw-r-- 1 myuser mygroup 22M may 29 10:04 f1.hex.xz
-rw-rw-r-- 1 myuser mygroup 42M may 29 10:04 f2.hex
-rw-rw-r-- 1 myuser mygroup 22M may 29 10:07 f2.hex.xz
-rw-rw-r-- 1 myuser mygroup 42M may 29 10:05 f3.hex
-rw-rw-r-- 1 myuser mygroup 22M may 29 10:07 f3.hex.xz

Данные стали действительно большими. Четыре раза в гексе, дважды, если гекс сжимается. Теперь самое интересное: давайте вычислим разницу между гексом и сжимаем это:

$ diff f{1,2}.hex | tee f1-f2.diff | xz -1 > f1-f2.diff.xz
$ diff f{1,3}.hex | tee f1-f3.diff | xz -1 > f1-f3.diff.xz
$ ls -lh f1-*
-rw-rw-r-- 1 myuser mygroup 7,8K may 29 10:04 f1-f2.diff
-rw-rw-r-- 1 myuser mygroup 4,3K may 29 10:06 f1-f2.diff.xz
-rw-rw-r-- 1 myuser mygroup 2,6K may 29 10:06 f1-f3.diff
-rw-rw-r-- 1 myuser mygroup 1,7K may 29 10:06 f1-f3.diff.xz

И это мило. Подведем итоги:

$   # All you need to save to disk is this
$ du -cb f1{,-*z}
10485760        f1
4400    f1-f2.diff.xz
1652    f1-f3.diff.xz
10491812        total
$   # This is what you would have had to store
$ du -cb f{1..3}
10485760        f1
10485760        f2
10485760        f3
31457280        total
$   # Compared to "f2"'s original size, this is the percentage
$   #+ of all the new information you need to store about it
$ echo 'scale=4; 4400 * 100 / 31457280' | bc -l
.0419
$   # Compared to "f3"'s original size, this is the percentage
$   #+ of all the new information you need to store about it
$ echo 'scale=4; 1652 * 100 / 10485760' | bc -l
.0157
$   # So, compared to the grand total, this is the percetage
$   #+ of information you need to store 
$ echo 'scale=2; 10491812 * 100 / 10485760' | bc -l
33.35

Чем больше у вас файлов, тем лучше это работает. Чтобы сделать тест восстановления данных из ваших сжатых различий "f2":

$ xz -d < f1-f2.diff.xz > f1-f2.diff.restored
$   # Assuming you haven't deleted "f1.diff":
$ patch -o f2.hex.restored f1.hex f1-f2.diff.restored
patching file f1.hex
$ diff f2.hex.restored f2.hex # No diffs will be found unless corrupted
$ xxd -r f2.hex.restored f2.restored # We get the completely restored file
$ diff -q f2 f2.restored # No diffs will be found unless corrupted

ВЫСТУПЛЕНИЯ

  • Вам не нужны некоторые файлы, сгенерированные здесь, такие как сжатые версии исходных файлов и сжатый гекс. Я сделал это только для того, чтобы подчеркнуть.
  • Успех этого метода во многом зависит от значения "почти полностью идентичен". Вам нужно сделать тесты. Я сделал несколько тестов, и это отлично работает для многих типов данных (а именно, дампов базы данных и даже отредактированных изображений и видео). Я на самом деле использую это для некоторых резервных копий.
  • Более сложным методом является использование librsync, но он отлично работает во многих ситуациях и будет отлично работать практически в любой среде * nix без необходимости установки нового программного обеспечения.
  • С другой стороны, это может потребовать некоторых сценариев.
  • Я не знаю ни одного инструмента, который делает все это.
1

gzip работает с 32-килобайтными блоками, поэтому поможет, если те же шаблоны находятся в 32-килобайтном диапазоне (это не ваш случай). Для xz вы можете попробовать передать очень большой размер --block, но вам нужно много свободной памяти (см. Параметры --memlimit ).

Всё ещё ищете ответ? Посмотрите другие вопросы с метками .