Tại sao nén Gzip không loại bỏ các khối dữ liệu trùng lặp?


30

Tôi vừa thực hiện một thử nghiệm nhỏ trong đó tôi đã tạo một kho lưu trữ tar với các tệp trùng lặp để xem liệu nó có bị nén hay không, với sự kinh ngạc của tôi, thì không! Chi tiết theo dõi (kết quả thụt lề để đọc niềm vui):

$ dd if=/dev/urandom bs=1M count=1 of=a
  1+0 records in
  1+0 records out
  1048576 bytes (1.0 MB) copied, 0.114354 s, 9.2 MB/s
$ cp a b
$ ln a c
$ ll
  total 3072
  -rw-r--r-- 2 guido guido 1048576 Sep 24 15:51 a
  -rw-r--r-- 1 guido guido 1048576 Sep 24 15:51 b
  -rw-r--r-- 2 guido guido 1048576 Sep 24 15:51 c
$ tar -c * -f test.tar
$ ls -l test.tar 
  -rw-r--r-- 1 guido guido 2109440 Sep 24 15:51 test.tar
$ gzip test.tar 
$ ls -l test.tar.gz 
  -rw-r--r-- 1 guido guido 2097921 Sep 24 15:51 test.tar.gz
$ 

Đầu tiên tôi tạo một tệp 1MiB của dữ liệu ngẫu nhiên (a). Sau đó, tôi đã sao chép nó vào một tập tin b và cũng liên kết nó với c. Khi tạo tarball, tar rõ ràng nhận thức được liên kết cứng, vì tarball chỉ ~ 2MiB chứ không phải ~ 3Mib.

Bây giờ tôi dự kiến ​​gzip sẽ giảm kích thước của tarball xuống ~ 1MiB vì a và b là trùng lặp và sẽ có 1MiB dữ liệu liên tục được lặp lại bên trong tarball, nhưng điều này đã không xảy ra.

Tại sao lại thế này? Và làm thế nào tôi có thể nén tarball hiệu quả trong những trường hợp này?

Câu trả lời:


24

Gzip gzip dựa trên thuật toán DEFLATE, là sự kết hợp giữa mã hóa LZ77 và Huffman. Đó là một thuật toán nén dữ liệu không mất dữ liệu hoạt động bằng cách chuyển đổi luồng đầu vào thành các ký hiệu nén bằng một từ điển được xây dựng nhanh chóng và xem các bản sao. Nhưng nó không thể tìm thấy các bản sao cách nhau hơn 32K. Mong đợi nó phát hiện các bản sao cách nhau 1MB là không thực tế.


Đủ công bằng! Bạn có tình cờ biết bất kỳ sự thay thế nào không hoạt động trên các luồng không?
Guido

1
Tôi không biết bất kỳ giải pháp đóng gói cho vấn đề của bạn. Nếu tôi dự đoán đây sẽ là một sự cố nghiêm trọng lặp đi lặp lại, tôi (cá nhân) sẽ tấn công nó bằng một tập lệnh thực hiện các thao tác cmp (so sánh) n-way để tìm các bản sao, viết danh sách vào một tệp, sau đó chỉ tar + gzip mục độc đáo + danh sách. Để khôi phục, tôi sẽ sử dụng tập lệnh thứ hai để giải nén và giải nén, sau đó tạo các dups từ danh sách. Một cách khác là biến các dups thành các liên kết cứng, vì bạn biết tar không phát hiện ra các liên kết đó. Xin lỗi, tôi biết đó có lẽ không phải là điều bạn đang hy vọng.
Nicole Hamilton

1
Cả gzip và bzip2 đều phải tương đối "thân thiện với luồng" vì thiết kế của chúng - điều hoàn toàn cần thiết để có thể hoạt động như một phần của đường ống. Những gì bạn đang tìm kiếm ở đây thực sự là sự trùng lặp và không chỉ là nén. Vì tar chia quá trình thành hai phần - chỉ lưu trữ bằng tar, và sau đó sử dụng chương trình thứ hai làm bộ lọc để nén. Tôi không thể tìm thấy bất kỳ kho lưu trữ nén nào với sự trùng lặp trong các tìm kiếm của mình, nhưng tôi đã tìm thấy câu hỏi liên quan trước đó. superuser.com/questions/286414/ trộm
Stephanie

2
@Stephanie, Nicole Hamilton: Có en.wikipedia.org/wiki/Lrzip#Lrzip .
Ốc cơ khí

1
@Guido Tất nhiên không có gì có thể loại bỏ các bản sao của thứ gì đó mà nó không nhớ trong luồng, nhưng hãy thử một cái gì đó như xz -9 -M 95%, hoặc thậm chí xz -M 95% --lzma2=preset=9,dict=1610612736. Nó sẽ không nhanh, nhưng kết quả trùng lặp của bạn không có khả năng bị bỏ lại trong kết quả.
Eroen

39

Nicole Hamilton lưu ý chính xác rằng gzipsẽ không tìm thấy dữ liệu trùng lặp xa do kích thước từ điển nhỏ của nó.

bzip2 tương tự, vì nó giới hạn ở 900 KB bộ nhớ.

Thay vào đó, hãy thử:

Thuật toán LZMA / LZMA2 ( xz, 7z)

Thuật toán LZMA cùng họ với Deflate, nhưng sử dụng kích thước từ điển lớn hơn nhiều (có thể tùy chỉnh; mặc định là khoảng 384 MB). Các xztiện ích, cần được cài đặt theo mặc định trên hầu hết các distro Linux gần đây, cũng tương tự nhưgzip và sử dụng LZMA.

Khi LZMA phát hiện dự phòng phạm vi dài hơn, nó sẽ có thể sao chép dữ liệu của bạn tại đây. Tuy nhiên, nó chậm hơn Gzip.

Một tùy chọn khác là 7-zip ( 7z, trong p7zipgói), đó là một bộ lưu trữ (chứ không phải là máy nén một luồng) sử dụng LZMA theo mặc định (được viết bởi tác giả của LZMA). Bộ lưu trữ 7-zip chạy sự trùng lặp của chính nó ở cấp độ tệp (nhìn vào các tệp có cùng phần mở rộng) khi lưu trữ theo .7zđịnh dạng của nó . Điều này có nghĩa rằng nếu bạn sẵn sàng để thay thế tarvới 7z, bạn sẽ có được các tập tin trùng loại bỏ trùng lặp. Tuy nhiên, 7z không bảo toàn dấu thời gian nano giây, quyền hoặc xattrs, vì vậy nó có thể không phù hợp với nhu cầu của bạn.

lrzip

lrziplà một máy nén xử lý trước dữ liệu để loại bỏ sự dư thừa đường dài trước khi đưa nó vào một thuật toán thông thường như Gzip / Deflate, bzip2, lzop hoặc LZMA. Đối với dữ liệu mẫu bạn cung cấp ở đây, không cần thiết; nó hữu ích khi dữ liệu đầu vào lớn hơn dữ liệu có thể vừa trong bộ nhớ.

Đối với loại dữ liệu này (các khối không thể sao chép trùng lặp), bạn nên sử dụng lzopnén (rất nhanh) với lrzip, vì không có ích gì khi cố gắng nén dữ liệu hoàn toàn ngẫu nhiên một khi đã bị trùng lặp.

Bup và Obnam

Vì bạn đã gắn thẻ câu hỏi , nếu mục tiêu của bạn ở đây là sao lưu dữ liệu, hãy xem xét sử dụng chương trình sao lưu trùng lặp như Bup hoặc Obnam .


Lrzip này có vẻ thú vị. Nó thậm chí có một tác giả được biết đến với các giải pháp phi truyền thống. Bây giờ tôi sẽ phải sửa lại các tập lệnh sao lưu của mình. Lần nữa.
Eroen

3
+1 Wow, thật là một nguồn kiến ​​thức / kinh nghiệm ở đó. Đánh giá cao. Tôi có thể thêm các hệ thống tập tin được kích hoạt vào hỗn hợp không? ZFS (và, tôi nghĩ Btrfs dự kiến ​​sẽ có nó) - sẽ hoạt động với sao chép được căn chỉnh theo khối
sehe

7Zip sử dụng nén LZMA2 và kích thước dicctionary 1536Mb (kích thước tối đa có sẵn trong Windows GUI) hoạt động rất tốt đối với tôi!
Leopoldo Sanchot

2

Trong trường hợp sao lưu, có thể với một tập hợp lớn các tệp nhỏ hơn, một mẹo có thể phù hợp với bạn là sắp xếp các tệp trong tar theo tiện ích mở rộng:

find archive_dir -type f | rev | sort | rev | tar czf my_archive.tar.gz -I -

Tôi đã cắt bỏ tất cả rev(tại sao thậm chí đảo ngược rồi sắp xếp?) Và xem sorttùy chọn "-r, --reverse" (mặc dù tôi không chắc tại sao bạn thậm chí muốn đảo ngược). Nhưng tôi nghĩ rằng tartùy chọn của bạn " -I" không làm những gì bạn nghĩ " -I, --use-compress-program PROG" , bạn có thể muốn "-T, --files-from FILE"
Xen2050

Tôi tin là | tar czf my_archive.tar.gz -I -nên như vậy| xargs tar Azf my_archive.tar.gz
Olivier Dulac

@ Xen2050, revđảo ngược thứ tự của các ký tự trong mỗi dòng, không phải thứ tự dòng trong luồng. Bởi vì điều này, sortnhóm các tập tin theo phần mở rộng của họ. Tôi nghi ngờ -I -nên có -T -, trong đó cung cấp danh sách tập tin trên stdin.
billyjmc

@billyjmc Tôi thấy, đó revsẽ là cách sắp xếp theo phần mở rộng, không phải là có nhiều phần mở rộng trong linux. Tôi tưởng tượng việc sắp xếp theo kích thước sẽ có cơ hội tìm kiếm song sinh cao hơn
Xen2050

2

gzipsẽ không tìm thấy bản sao, ngay cả xzvới kích thước từ điển lớn sẽ không. Những gì bạn có thể làm là sử dụng mksquashfs- điều này thực sự sẽ tiết kiệm không gian của các bản sao.

Một số kết quả kiểm tra nhanh với xzmksquashfsvới ba tệp nhị phân ngẫu nhiên (64 MB) trong đó hai tệp giống nhau:

Thiết lập:

mkdir test
cd test
dd if=/dev/urandom of=test1.bin count=64k bs=1k
dd if=/dev/urandom of=test2.bin count=64k bs=1k
cp test{2,3}.bin
cd ..

Bóng quần:

mksquashfs test/ test.squash
> test.squash - 129M

xz:

XZ_OPT='-v --memlimit-compress=6G --memlimit-decompress=512M --lzma2=preset=9e,dict=512M --extreme -T4 ' tar -cJvf test.tar.xz test/
> test.tar.xz - 193M

Có phải mksquashfs chỉ tìm thấy các bản sao ở cấp độ tệp hoặc nó cũng hoạt động trên các phần nhỏ hơn? Đó là: Nó cũng sẽ nén các tệp hơi khác nhau nhưng chủ yếu là giống nhau chứ?
Chaos_99

Điều này chỉ hoạt động trên cơ sở tập tin. Bạn có thể thấy rằng khi taring ba tệp thử nghiệm đó vào kho lưu trữ tar không nén và nén chúng bằng mksquashfs sau đó. Mặt khác, mksqashfs sẽ báo cáo, khi tìm thấy các bản sao với Number of duplicate files foundtrong thiết bị xuất chuẩn.
Izzy

1

Trên hệ thống của tôi, lzma test.tarkết quả là tệp test.tar.lzma 106'3175 byte (1.1M)


1

Là một bổ sung cho 'câu trả lời của ốc cơ khí:

Ngay cả xz (hoặc lzma) sẽ không tìm thấy các bản sao nếu kích thước tệp của tệp đơn không nén (hoặc chính xác hơn là khoảng cách giữa các bản sao) vượt quá kích thước từ điển. xz (hoặc lzma) ngay cả ở cài đặt cao nhất-9e chỉ dự trữ 64 MB cho việc này.

May mắn thay, bạn có thể chỉ định kích thước dictonary của riêng bạn với tùy chọn --lzma2=dict=256MB (chỉ--lzma1=dict=256MB được phép khi sử dụng bí danh lzma cho lệnh)

Thật không may, khi ghi đè cài đặt bằng chuỗi nén tùy chỉnh như được nêu trong ví dụ trên, các giá trị mặc định cho tất cả các tham số khác không được đặt ở cùng mức như với -9e. Vì vậy, mật độ nén không cao bằng các tệp đơn.


-2

gzip không có công tắc dòng lệnh sử dụng thuật toán thấp nhất có thể để nén.

Hãy thử sử dụng:

gzip -9 test.tar

Bạn sẽ nhận được kết quả tốt hơn


1
Không thực sự, sự khác biệt là tối thiểu. Tôi cũng đã thử bzip2 với kết quả tương tự.
Guido

gzip không có công tắc dòng lệnh sử dụng thuật toán thấp nhất có thể để nén. => Điều này không đúng - "man gzip" nói rằng "(t) mức nén mặc định của anh ta là -6 (nghĩa là thiên về nén cao với chi phí tốc độ)." Điều này đúng với tất cả các phiên bản gzip mà tôi biết, nếu cài đặt mặc định được biên dịch không bị ghi đè bởi biến môi trường GZIP. Ngay cả cấp độ "-9" sẽ không giúp bạn ở đây, như đã được giải thích trong các câu trả lời đã cho.
Gunter Ohrner
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.