Hàng triệu tệp văn bản (nhỏ) trong một thư mục


15

Chúng tôi muốn lưu trữ hàng triệu tệp văn bản trong hệ thống tệp Linux, với mục đích có thể nén và phục vụ một bộ sưu tập tùy ý như một dịch vụ. Chúng tôi đã thử các giải pháp khác, như cơ sở dữ liệu khóa / giá trị, nhưng các yêu cầu của chúng tôi về tính đồng thời và song song làm cho việc sử dụng hệ thống tệp gốc là lựa chọn tốt nhất.

Cách đơn giản nhất là lưu trữ tất cả các tệp trong một thư mục:

$ ls text_files/
1.txt
2.txt
3.txt

thể có trên hệ thống tệp EXT4 , không giới hạn số lượng tệp trong một thư mục.

Hai quy trình FS sẽ là:

  1. Viết tệp văn bản từ web scrape (không nên bị ảnh hưởng bởi số lượng tệp trong thư mục).
  2. Zip tập tin được chọn, được đưa ra bởi danh sách tên tập tin.

Câu hỏi của tôi là, việc lưu trữ tới mười triệu tệp trong một thư mục có ảnh hưởng đến hiệu suất của các hoạt động trên hoặc hiệu năng hệ thống chung, khác với việc tạo một cây thư mục con cho các tệp để sống không?


4
Liên quan: Cách sửa lỗi gián đoạn Không còn chỗ trống trên thiết bị Lỗi trong khi mv khi thiết bị có nhiều dung lượng . Việc sử dụng dir_index, thường được bật theo mặc định, sẽ tăng tốc độ tra cứu nhưng có thể giới hạn số lượng tệp trên mỗi thư mục.
Đánh dấu Plotnick

Tại sao không thử nhanh trên máy ảo và xem nó như thế nào? Với bash, việc đặt một thư mục chứa một triệu tệp văn bản với các ký tự ngẫu nhiên bên trong là chuyện nhỏ. Tôi cảm thấy như bạn sẽ nhận được thông tin thực sự hữu ích theo cách đó, ngoài những gì bạn sẽ học ở đây.
JoshuaD

2
@JoshuaD: Nếu bạn nhập tất cả cùng một lúc vào một FS mới, bạn có khả năng có tất cả các nút tiếp giáp trên đĩa, do đó ls -l, hoặc bất cứ thứ gì khác statđược inode trong thư mục (ví dụ: bashhoàn thành toàn cầu / tab) sẽ nhanh hơn một cách giả tạo hơn sau khi hao mòn (xóa một số tệp, viết một số tệp mới). ext4 có thể làm tốt hơn với điều này so với XFS, bởi vì XFS tự động phân bổ không gian cho các nút so với dữ liệu, vì vậy bạn có thể kết thúc với các nút bị phân tán nhiều hơn, tôi nghĩ vậy. (Nhưng đó là một phỏng đoán thuần túy dựa trên rất ít kiến ​​thức chi tiết; Tôi hầu như không sử dụng ext4). Đi với abc/def/subirs.
Peter Cordes

Phải, tôi không nghĩ thử nghiệm mà tôi đề xuất sẽ có thể nói với OP "điều này sẽ hiệu quả", nhưng nó chắc chắn có thể nhanh chóng nói với anh ta "điều này sẽ không hiệu quả", rất hữu ích.
JoshuaD

1
nhưng các yêu cầu của chúng tôi về đồng thời và song song làm cho việc sử dụng hệ thống tệp gốc là lựa chọn tốt nhất Bạn đã thử gì? Ăn nói lấc cấc, tôi nghĩ ngay cả một cấp thấp hơn RDBMS như MySQL và một servlet Java tạo ra các file zip một cách nhanh chóng vớiZipOutputStream sẽ đánh bại chỉ là về bất kỳ miễn phí Linux hệ thống tập tin bản địa - tôi nghi ngờ bạn muốn trả tiền cho GPFS của IBM. Vòng lặp để xử lý tập kết quả JDBC và tạo luồng zip đó có lẽ chỉ là 6-8 dòng mã Java.
Andrew Henle

Câu trả lời:


10

Các lslệnh, hoặc thậm chí TAB hoàn thành hoặc mở rộng ký tự đại diện bởi vỏ, thường sẽ trình bày kết quả theo thứ tự chữ và số. Điều này đòi hỏi phải đọc toàn bộ danh sách thư mục và sắp xếp nó. Với mười triệu tệp trong một thư mục, thao tác sắp xếp này sẽ mất một lượng thời gian không đáng kể.

Nếu bạn có thể chống lại sự thôi thúc hoàn thành TAB và ví dụ: viết tên của các tệp sẽ được nén đầy đủ, sẽ không có vấn đề gì.

Một vấn đề khác với ký tự đại diện có thể là việc mở rộng ký tự đại diện có thể tạo ra nhiều tên tệp hơn sẽ phù hợp với dòng lệnh có độ dài tối đa. Độ dài dòng lệnh tối đa điển hình sẽ là quá đủ cho hầu hết các tình huống, nhưng khi chúng ta nói về hàng triệu tệp trong một thư mục, đây không còn là một giả định an toàn. Khi vượt quá độ dài dòng lệnh tối đa trong việc mở rộng ký tự đại diện, hầu hết các shell sẽ chỉ đơn giản là thất bại toàn bộ dòng lệnh mà không thực hiện nó.

Điều này có thể được giải quyết bằng cách thực hiện các thao tác ký tự đại diện của bạn bằng cách sử dụng findlệnh:

find <directory> -name '<wildcard expression>' -exec <command> {} \+

hoặc một cú pháp tương tự bất cứ khi nào có thể. Các find ... -exec ... \+sẽ tự động đưa vào tài khoản theo chiều dài dòng lệnh tối đa, và sẽ thực hiện lệnh nhiều lần theo yêu cầu trong khi lắp số lượng tối đa của tên tập tin cho mỗi dòng lệnh.


Các hệ thống tập tin hiện đại sử dụng B, B + hoặc các cây tương tự để giữ các mục nhập thư mục. vi.wikipedia.org/wiki/HTree
dimm

4
Đúng ... nhưng nếu trình bao hoặc lslệnh sẽ không biết rằng danh sách thư mục đã được sắp xếp, họ sẽ dành thời gian để chạy thuật toán sắp xếp. Và bên cạnh đó, không gian người dùng có thể đang sử dụng một thứ tự sắp xếp cục bộ (LC_COLLATE) có thể khác với những gì hệ thống tập tin có thể làm trong nội bộ.
telcoM

17

Điều này rất gần với một câu hỏi / câu trả lời dựa trên ý kiến ​​nhưng tôi sẽ cố gắng cung cấp một số sự kiện với ý kiến ​​của tôi.

  1. Nếu bạn có số lượng tệp rất lớn trong một thư mục, mọi thao tác dựa trên trình bao cố gắng liệt kê chúng (ví dụ mv * /somewhere/else) có thể không mở rộng ký tự đại diện thành công hoặc kết quả có thể quá lớn để sử dụng.
  2. ls sẽ mất nhiều thời gian hơn để liệt kê một số lượng tệp rất lớn so với một số lượng nhỏ tệp.
  3. Hệ thống tập tin sẽ có thể xử lý hàng triệu tệp trong một thư mục, nhưng mọi người có thể sẽ phải vật lộn.

Một khuyến nghị là chia tên tệp thành hai, ba hoặc bốn ký tự và sử dụng chúng làm thư mục con. Ví dụ, somefilename.txtcó thể được lưu trữ dưới dạng som/efi/somefilename.txt. Nếu bạn đang sử dụng tên số thì chia từ phải sang trái thay vì trái sang phải để có phân phối đồng đều hơn. Ví dụ 12345.txtcó thể được lưu trữ dưới dạng 345/12/12345.txt.

Bạn có thể sử dụng tương đương zip -j zipfile.zip path1/file1 path2/file2 ...để tránh bao gồm các đường dẫn thư mục con trung gian trong tệp ZIP.

Nếu bạn đang phục vụ các tệp này từ một máy chủ web (tôi không hoàn toàn chắc chắn liệu điều đó có liên quan hay không) thì việc che giấu cấu trúc này có lợi cho một thư mục ảo với các quy tắc viết lại trong Apache2 là chuyện nhỏ. Tôi cho rằng điều tương tự cũng đúng với Nginx.


Việc *mở rộng sẽ thành công trừ khi bạn hết bộ nhớ, nhưng trừ khi bạn tăng giới hạn ngăn xếp (trên Linux) hoặc sử dụng trình bao mvđược dựng sẵn hoặc có thể được xây dựng (ksh93, zsh), execve()cuộc gọi hệ thống có thể thất bại với lỗi E2BIG.
Stéphane Chazelas

@ StéphaneChazelas vâng, ok, sự lựa chọn từ ngữ của tôi có thể tốt hơn, nhưng hiệu ứng ròng cho người dùng là như nhau. Tôi sẽ xem liệu tôi có thể thay đổi các từ một chút mà không bị sa lầy vào sự phức tạp.
roaima

Chỉ tò mò làm thế nào bạn sẽ giải nén tệp zip đó nếu bạn tránh bao gồm các đường dẫn thư mục con trung gian trong đó, mà không chạy vào các vấn đề bạn thảo luận?
Bạch tuộc

1
@Octopus OP tuyên bố rằng tệp zip sẽ chứa " các tệp được chọn, được đưa ra bởi danh sách tên tệp ".
roaima

Tôi khuyên bạn nên sử dụng zip -j - ...và dẫn luồng đầu ra trực tiếp đến kết nối mạng của khách hàng zip -j zipfile.zip .... Ghi một tệp zip thực tế vào đĩa có nghĩa là đường dẫn dữ liệu được đọc từ đĩa-> nén-> ghi vào đĩa-> đọc từ đĩa-> gửi đến máy khách. Điều đó có thể tăng gấp ba lần yêu cầu IO của đĩa so với đọc từ đĩa-> nén-> gửi cho khách hàng.
Andrew Henle

5

Tôi điều hành một trang web xử lý cơ sở dữ liệu cho phim, TV và trò chơi video. Đối với mỗi trong số này, có nhiều hình ảnh với TV chứa hàng tá hình ảnh cho mỗi chương trình (ví dụ: ảnh chụp nhanh tập, v.v.).

Cuối cùng có rất nhiều tập tin hình ảnh. Một nơi nào đó trong phạm vi 250.000+. Tất cả đều được lưu trữ trong một thiết bị lưu trữ khối gắn kết trong đó thời gian truy cập là hợp lý.

Nỗ lực đầu tiên của tôi trong việc lưu trữ hình ảnh là trong một thư mục duy nhất là /mnt/images/UUID.jpg

Tôi chạy vào những thử thách sau.

  • lsthông qua một thiết bị đầu cuối từ xa sẽ chỉ treo. Quá trình sẽ biến thành zombie và CTRL+Csẽ không phá vỡ nó.
  • trước khi tôi đạt đến điểm đó, bất kỳ lslệnh nào cũng sẽ nhanh chóng lấp đầy bộ đệm đầu ra và CTRL+Csẽ không dừng việc cuộn vô tận.
  • Việc nén 250.000 tệp từ một thư mục mất khoảng 2 giờ. Bạn phải chạy lệnh zip tách ra khỏi thiết bị đầu cuối nếu không có bất kỳ gián đoạn nào trong kết nối có nghĩa là bạn phải bắt đầu lại.
  • Tôi sẽ không mạo hiểm khi sử dụng tệp zip trên Windows.
  • Các thư mục nhanh chóng trở thành một khu vực không cho phép con người .

Cuối cùng tôi đã phải lưu trữ các tệp trong các thư mục con bằng cách sử dụng thời gian tạo để tạo đường dẫn. Chẳng hạn như /mnt/images/YYYY/MM/DD/UUID.jpg. Điều này đã giải quyết tất cả các vấn đề trên và cho phép tôi tạo các tệp zip nhắm mục tiêu một ngày.

Nếu định danh duy nhất cho một tệp bạn có là một số và những số này có xu hướng chạy theo thứ tự. Tại sao không nhóm chúng theo 100000, 100001000.

Ví dụ: nếu bạn có một tệp có tên 384295.txt đường dẫn sẽ là:

/mnt/file/300000/80000/4000/295.txt

Nếu bạn biết bạn sẽ đạt được một vài triệu. Sử dụng 0tiền tố cho 1.000.000

/mnt/file/000000/300000/80000/4000/295.txt

1

Viết tệp văn bản từ web scrape (không nên bị ảnh hưởng bởi số lượng tệp trong thư mục).

Để tạo một tệp mới, yêu cầu quét tệp thư mục tìm đủ không gian trống cho mục nhập thư mục mới. Nếu không có khoảng trống nào đủ lớn để lưu mục nhập thư mục mới, nó sẽ được đặt ở cuối tệp thư mục. Khi số lượng tệp trong một thư mục tăng lên, thời gian để quét thư mục cũng tăng lên.

Miễn là các tệp thư mục vẫn còn trong bộ đệm của hệ thống, hiệu năng đạt được từ điều này sẽ không tệ, nhưng nếu dữ liệu được giải phóng, việc đọc tệp thư mục (thường bị phân mảnh cao) từ đĩa có thể tiêu tốn khá nhiều thời gian. Một ổ SSD cải thiện điều này, nhưng đối với một thư mục có hàng triệu tệp, vẫn có thể có một hiệu suất đáng chú ý.

Zip tập tin được chọn, được đưa ra bởi danh sách tên tập tin.

Điều này cũng có thể yêu cầu thêm thời gian trong một thư mục có hàng triệu tệp. Trong một hệ thống tệp với các mục nhập thư mục được băm (như EXT4), sự khác biệt này là tối thiểu.

việc lưu trữ tối đa mười triệu tệp trong một thư mục có ảnh hưởng đến hiệu suất của các hoạt động trên hoặc hiệu năng hệ thống nói chung, khác với việc tạo một cây thư mục con cho các tệp để sống không?

Một cây của các thư mục con không có nhược điểm hiệu suất nào ở trên. Ngoài ra, nếu hệ thống tệp cơ bản được thay đổi thành không có tên tệp băm, phương thức cây vẫn sẽ hoạt động tốt.


1

Thứ nhất: ngăn 'ls' sắp xếp với 'ls -U', có thể cập nhật ~ / bashrc của bạn để có 'bí danh ls = "ls -U"' hoặc tương tự.

Đối với tập tin lớn của bạn, bạn có thể thử như thế này:

  • tạo một tập các tệp kiểm tra

  • xem nếu nhiều tên tập tin gây ra vấn đề

  • sử dụng xargs parmeter-batching và zip (mặc định) hành vi thêm tệp vào zip để tránh sự cố.

Điều này làm việc tốt:

# create ~ 100k files
seq 1 99999 | sed "s/\(.*\)/a_somewhat_long_filename_as_a_prefix_to_exercise_zip_parameter_processing_\1.txt/" | xargs touch
# see if zip can handle such a list of names
zip -q /tmp/bar.zip ./*
    bash: /usr/bin/zip: Argument list too long
# use xargs to batch sets of filenames to zip
find . -type f | xargs zip -q /tmp/foo.zip
l /tmp/foo.zip
    28692 -rw-r--r-- 1 jmullee jmullee 29377592 2017-12-16 20:12 /tmp/foo.zip
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.