Давайте начнем с файла 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 без необходимости установки нового программного обеспечения.
- С другой стороны, это может потребовать некоторых сценариев.
- Я не знаю ни одного инструмента, который делает все это.