Bir klasördeki milyonlarca (küçük) metin dosyası


15

Hizmet olarak rasgele bir koleksiyon oluşturmak ve sunmak amacıyla milyonlarca metin dosyasını bir Linux dosya sisteminde depolamak istiyoruz. Bir anahtar / değer veritabanı gibi diğer çözümleri denedik, ancak eşzamanlılık ve paralellik gereksinimlerimiz yerel dosya sistemini kullanmayı en iyi seçim haline getiriyor.

En basit yol, tüm dosyaları bir klasörde saklamaktır:

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

bu da bir klasördeki dosya sayısında sınırlama bulunmayan bir EXT4 dosya sisteminde mümkün olmalıdır .

İki FS işlemi şöyle olacaktır:

  1. Web kazımaktan metin dosyası yazın (klasördeki dosya sayısından etkilenmemelidir).
  2. Dosya adları listesine göre seçilen dosyaları sıkıştırın.

Sorum şu ki, bir klasörde on milyona kadar dosya depolamak, yukarıdaki işlemlerin performansını veya genel sistem performansını etkileyecek, dosyaların yaşamak için bir alt klasör ağacı yapmaktan farklı mı?


4
İlgili: Cihazda yeterli alan olduğunda mv sırasında aralıklı “Cihazda alan kalmadı” hataları nasıl düzeltilir ? Kullanılması dir_indexaramalarını hızlandırır, genellikle varsayılan olarak etkin olduğu, ancak dizindeki başına dosya sayısını sınırlayabilir.
Mark Plotnick

Neden sanal bir makinede hızlı bir şekilde denemiyor ve neye benzediğini görmüyorsunuz? Bash ile bir klasörü içinde rastgele karakterler içeren bir milyon metin dosyası ile doldurmak önemsizdir. Burada öğreneceklerinize ek olarak bu şekilde gerçekten faydalı bilgiler edineceğinizi düşünüyorum.
JoshuaD

2
@JoshuaD: Bir taze FS tek seferde doldurmak, diskteki bitişik, bu nedenle tüm inode'lara olması olasıdır ls -lveya başka bir şey statdizindeki her düğüm (örn s bashyapay hızlı olacaktır globbing / sekme tamamlama) bazı aşınma ve yıpranma sonrası (bazı dosyaları silin, yeni dosyaları yazın). ext4 bunu XFS'den daha iyi yapabilir, çünkü XFS dinamik olarak indotalar ve veriler için alan ayırır, böylece inotlarla daha dağınık olabileceğini düşünüyorum. (Ama bu çok az detaylı bilgiye dayanan saf bir tahmin; ext4'i neredeyse hiç kullanmadım). abc/def/Alt dizinlerle git .
Peter Cordes

Evet, önerdiğim testin OP'ye "bu işe yarayacağını" söyleyebileceğini sanmıyorum, ama kesinlikle ona "bu işe yaramayacak" diyebilir ki bu yararlıdır.
JoshuaD

1
ancak eşzamanlılık ve paralellik gereksinimlerimiz yerel dosya sistemini kullanmayı en iyi seçim haline getiriyor Ne denediniz? Offhand, hatta MySQL gibi bir alt uç RDBMS ve zip dosyalarını anındaZipOutputStream oluşturan bir Java sunucu uygulaması bile herhangi bir ücretsiz Linux yerel dosya sistemi hakkında yenecekti - IBM'in GPFS'si için ödeme yapmak istediğinizden şüpheliyim. JDBC sonuç kümesini işlemek ve bu zip akışını yapmak için döngü yalnızca 6-8 satır Java kodudur.
Andrew Henle

Yanıtlar:


10

lsKomut, hatta TAB tamamlama veya kabuk tarafından Joker genişlemesi normal alfasayısal bunların sonuçları sunacak. Bu, tüm dizin listesinin okunmasını ve sıralanmasını gerektirir. Tek bir dizinde on milyon dosya olduğunda, bu sıralama işlemi önemsiz bir zaman alacaktır.

SEKME tamamlama isteğine direnebilir ve örneğin sıkıştırılacak dosyaların adlarını tam olarak yazabiliyorsanız, sorun olmamalıdır.

Joker karakterlerle ilgili bir başka sorun, büyük olasılıkla bir maksimum uzunluk komut satırına sığacakdan daha fazla dosya adı üreten joker karakter genişletmesi olabilir. Tipik maksimum komut satırı uzunluğu çoğu durum için yeterli olandan daha fazla olacaktır, ancak tek bir dizindeki milyonlarca dosyadan bahsederken, artık güvenli bir varsayım değildir. Joker karakter genişletmesinde bir maksimum komut satırı uzunluğu aşıldığında, çoğu kabuk yürütmeden tüm komut satırında başarısız olur.

Bu, joker karakter işlemlerinizi find komutu :

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

veya mümkün olduğunda benzer bir sözdizimi. find ... -exec ... \+Otomatik olarak dikkate maksimum komut satırı uzunluğunu alacak ve her komut satırına dosya adları maksimum miktarda uydurma ederken gerekli olduğu kadar çok kez komutu yerine getirecektir.


Modern dosya sistemleri dizin girişlerini tutmak için B, B + veya benzer ağaçları kullanır. en.wikipedia.org/wiki/HTree
dimm

4
Evet ... ancak kabuk veya lskomut dizin listelemesinin zaten sıralandığını bilmezse, sıralama algoritmasını çalıştırmak için zaman alacaktır. Ayrıca, kullanıcı alanı, dosya sisteminin dahili olarak yapabileceklerinden farklı olabilecek yerelleştirilmiş bir sıralama düzeni (LC_COLLATE) kullanıyor olabilir.
telcoM

17

Bu, düşünceye dayalı bir soru / cevaba tehlikeli bir şekilde yakın, ancak fikirlerimle bazı gerçekler sunmaya çalışacağım.

  1. Bir klasörde çok fazla sayıda dosyanız varsa, bunları numaralandırmaya çalışan kabuk tabanlı herhangi bir işlem (ör. mv * /somewhere/else ) Joker karakteri başarıyla genişletemeyebilir veya sonuç kullanmak için çok büyük olabilir.
  2. ls çok sayıda dosyayı numaralandırmak, az sayıda dosyadan daha uzun sürer.
  3. Dosya sistemi, tek bir dizinde milyonlarca dosyayı işleyebilecek, ancak insanlar muhtemelen mücadele edecek.

Bir öneri, dosya adını iki, üç veya dört karakterlik parçalara bölmek ve bunları alt dizinler olarak kullanmaktır. Örneğin, somefilename.txtolarak depolanabilir som/efi/somefilename.txt. Sayısal adlar kullanıyorsanız, daha eşit bir dağılım olması için soldan sağa doğru sağdan sola bölün. Örneğin 12345.txt, olarak depolanabilir 345/12/12345.txt.

zip -j zipfile.zip path1/file1 path2/file2 ...ZIP dosyasına ara alt dizin yollarının eklenmesini önlemek için ile eşdeğerini kullanabilirsiniz .

Bu dosyaları bir web sunucusundan sunuyorsanız (bunun ilgili olup olmadığından tam olarak emin değilim) Apache2'de yeniden yazma kuralları olan sanal bir dizin lehine bu yapıyı gizlemek önemsizdir. Aynı şeyin Nginx için de geçerli olduğunu varsayabilirim.


*Bellek tükendi sürece genişleme başarılı olur, ancak (Linux üzerinde) yığıt sınırını yükseltmek veya bir kabuk kullanmadığınız sürece mvyerleşiğidir ya da (ksh93, zsh) yerleşik olabilir, execve()sistem çağrısı bir E2BIG hatasıyla başarısız olabilir.
Stéphane Chazelas

@ StéphaneChazelas evet tamam, kelime seçimim daha iyi olabilirdi, ancak kullanıcı için net etki çok aynı. Karmaşıklığa girmeden kelimeleri biraz değiştirip değiştiremeyeceğimi göreceğim.
roaima

Tartıştığınız sorunlara girmeden ara alt dizin yollarını dahil etmekten kaçınırsanız, bu zip dosyasını nasıl açacağınızı merak ediyorsunuz?
Ahtapot

1
@Optopus OP, zip dosyasının " dosya adları listesine göre seçilen dosyaları " içereceğini belirtir .
roaima

zip -j - ...Çıktı akışını doğrudan istemcinin ağ bağlantısına kullanmayı ve borulamayı öneriyorum zip -j zipfile.zip .... Diske gerçek bir zip dosyası yazmak, veri yolunun disk-> sıkıştır-> diske yaz-> diskten okun-> istemciye gönder olduğu anlamına gelir. Bu, disk IO gereksinimlerinizi disk-> sıkıştır-> istemciye gönder üzerinden üç katına çıkarabilir .
Andrew Henle

5

Filmler, TV ve video oyunları için bir veritabanı işleyen bir web sitesi işletiyorum. Bunların her biri için TV'de şov başına düzinelerce görüntü içeren çoklu görüntüler vardır (yani bölüm anlık görüntüleri vb.).

Sonuçta bir sürü resim dosyası oluyor. 250.000'in üzerinde bir yerde. Bunların tümü, erişim süresinin makul olduğu monte edilmiş bir blok depolama cihazında saklanır.

Görüntüleri saklamak için ilk denemem, /mnt/images/UUID.jpg

Aşağıdaki zorluklarla karşılaştım.

  • lsuzak bir terminal aracılığıyla sadece asmak. Süreç zombi olacak ve CTRL+Conu kırmayacaktı.
  • Bu noktaya gelmeden önce herhangi bir lskomut hızlı bir şekilde çıktı arabelleğini doldurur CTRL+Cve sonsuz kaydırmayı durdurmazdı.
  • 250.000 dosyayı tek bir klasörden sıkıştırmak yaklaşık 2 saat sürdü. Terminalden ayrılmış zip komutunu çalıştırmanız gerekir, aksi takdirde bağlantıdaki herhangi bir kesinti yeniden başlamanız gerektiği anlamına gelir.
  • Zip dosyasını Windows'ta kullanmaya çalışma riskim olmazdı.
  • Klasör hızla insanların girmesine izin verilmeyen bir bölge haline geldi .

Yol oluşturmak için oluşturma zamanını kullanarak dosyaları alt klasörlerde saklamak zorunda kaldım. Gibi /mnt/images/YYYY/MM/DD/UUID.jpg. Bu, yukarıdaki tüm sorunları çözdü ve bir tarihi hedefleyen zip dosyaları oluşturmama izin verdi.

Sahip olduğunuz bir dosya için tek tanımlayıcı sayısal bir sayıysa ve bu sayılar sırayla çalışma eğilimindedir. Neden grup onları 100000, 10000ve 1000.

Örneğin 384295.txt, yol adında bir dosyanız varsa :

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

Biliyorsan birkaç milyona ulaşacaksın. 01.000.000 için önek kullanın

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

1

Web kazımaktan metin dosyası yazın (klasördeki dosya sayısından etkilenmemelidir).

Yeni bir dosya oluşturmak için yeni dizin girişi için yeterli boş alan arayan dizin dosyasının taranması gerekir. Yeni dizin girdisini depolayacak kadar büyük yer yoksa, dizin dosyasının sonuna yerleştirilir. Bir dizindeki dosya sayısı arttıkça, dizini tarama süresi de artar.

Dizin dosyaları sistem önbelleğinde kaldığı sürece, bundan etkilenen performans kötü olmayacaktır, ancak veriler serbest bırakılırsa, dizin dosyasını (genellikle yüksek oranda parçalanmış) diskten okumak biraz zaman alabilir. Bir SSD bunu geliştirir, ancak milyonlarca dosya içeren bir dizin için yine de dikkat çekici bir performans isabeti olabilir.

Dosya adları listesine göre seçilen dosyaları sıkıştırın.

Bu aynı zamanda milyonlarca dosya içeren bir dizinde ek zaman gerektirir. Karma dizin girişlerine (EXT4 gibi) sahip bir dosya sisteminde bu fark minimumdur.

bir klasörde on milyon adede kadar dosya depolamak, yukarıdaki işlemlerin performansını veya genel sistem performansını etkileyecek, dosyaların yaşamak için bir alt klasör ağacı yapmaktan farklı olacak mı?

Bir alt klasör ağacının yukarıdaki performans dezavantajları yoktur. Buna ek olarak, temel dosya sistemi karma dosya adlarına sahip olmayacak şekilde değiştirilirse, ağaç yöntemi hala iyi çalışır.


1

İlk olarak: 'ls' 'ls -U' ile sıralama önlemek, belki ~ / bashrc 'takma ls = "ls -U"' veya benzeri olacak şekilde güncelleyin.

Büyük dosya kümeniz için bunu şu şekilde deneyebilirsiniz:

  • bir dizi test dosyası oluştur

  • birçok dosya adının soruna neden olup olmadığını görün

  • sorunlardan kaçınmak için xargs parmeter-batching ve zip'in zip'e dosya ekleme davranışını kullanın.

Bu iyi çalış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
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.