Tło: serwer fizyczny, około dwóch lat, dyski SATA 7200 obr./min podłączone do karty RAID 3Ware, noatime montowany przez ext3 FS i dane = zamówione, nie pod szalonym obciążeniem, jądro 2.6.18-92.1.22.el5, czas pracy 545 dni . Katalog nie zawiera żadnych podkatalogów, tylko miliony małych (~ 100 bajtów) plików, a niektóre większe (kilka KB).
Mamy serwer, który w ciągu ostatnich kilku miesięcy trochę poszedł na kukułkę, ale zauważyliśmy go dopiero wtedy, gdy zaczął nie móc pisać do katalogu z powodu zbyt dużej liczby plików. W szczególności zaczął rzucać ten błąd w / var / log / messages:
ext3_dx_add_entry: Directory index full!
Na dysku tym pozostało wiele i-węzłów:
Filesystem Inodes IUsed IFree IUse% Mounted on
/dev/sda3 60719104 3465660 57253444 6% /
Zgaduję, że to oznacza, że przekroczyliśmy limit liczby wpisów w samym pliku katalogu. Nie mam pojęcia, ile to plików, ale nie może to być, jak widać, więcej niż trzy miliony. Pamiętaj, że to nie jest dobre! Ale to pierwsza część mojego pytania: co to za górna granica? Czy można go przestroić? Zanim zacznę na mnie krzyczeć - chcę to wyciszyć ; ten ogromny katalog spowodował wiele problemów.
W każdym razie wyśledziliśmy problem w kodzie, który generował wszystkie te pliki, i naprawiliśmy go. Teraz utknąłem w usuwaniu katalogu.
Kilka opcji tutaj:
rm -rf (dir)
Najpierw tego spróbowałem. Zrezygnowałem i zabiłem go po półtora dnia bez widocznego uderzenia.
- unlink (2) w katalogu: Zdecydowanie warte rozważenia, ale pytanie brzmi, czy szybsze byłoby usunięcie plików w katalogu za pomocą fsck niż usunięcie za pomocą unlink (2). To znaczy w taki czy inny sposób muszę oznaczyć te i-węzły jako nieużywane. Zakłada się oczywiście, że mogę powiedzieć fsck, aby nie upuszczał wpisów do plików w / lost + found; w przeciwnym razie właśnie przeniosłem swój problem. Oprócz wszystkich innych obaw, po przeczytaniu o tym trochę więcej, okazuje się, że prawdopodobnie musiałbym wywołać niektóre wewnętrzne funkcje FS, ponieważ żaden z wariantów unlink (2), które mogę znaleźć, nie pozwoliłby mi po prostu beztrosko usunąć katalog z wpisami. Puchatek.
while [ true ]; do ls -Uf | head -n 10000 | xargs rm -f 2>/dev/null; done )
To jest właściwie skrócona wersja; prawdziwy, który uruchamiam, który po prostu dodaje raportowanie postępów i czyste zatrzymanie, gdy zabraknie plików do usunięcia, to:
eksport i = 0; time (while [true]; do ls -Uf | głowa -n 3 | grep -qF '.png' || przerwa; ls -Uf | głowa -n 10000 | xargs rm -f 2> / dev / null; eksport i = $ (($ i + 10000)); echo „$ i ...”; gotowy )
To wydaje się działać całkiem dobrze. Pisząc to, usunęło 260 000 plików w ciągu ostatnich trzydziestu minut.
- Jak wspomniano powyżej, czy można ograniczyć limit wpisów dla poszczególnych katalogów?
- Dlaczego usunięcie pojedynczego pliku, który był pierwszym z listy zwróconej przez
ls -U
, zajęło „prawdziwe 7m9.561s / użytkownik 0m0.001s / sys 0m0.001s” , a usunięcie pierwszych 10 000 wpisów za pomocą rozkaz w # 3, ale teraz idzie całkiem szczęśliwie? W tym przypadku usunął 260 000 w około trzydzieści minut, ale teraz zajęło kolejne piętnaście minut, aby usunąć kolejne 60 000. Dlaczego ogromne wahania prędkości? - Czy jest lepszy sposób na zrobienie czegoś takiego? Nie przechowuj milionów plików w katalogu; Wiem, że to głupie i nie zdarzyłoby się to na moim zegarku. Googlowanie problemu i przeglądanie SF i SO oferuje wiele wariantów
find
, które nie będą znacznie szybsze niż moje podejście z kilku oczywistych powodów. Ale czy pomysł usuwania przez fsck ma jakieś nogi? A może coś zupełnie innego? Nie mogę się doczekać, aby usłyszeć myślenie „po wyjęciu z pudełka” (lub w „mało znanym” pudełku).
Ostateczne wyjście skryptu !:
2970000...
2980000...
2990000...
3000000...
3010000...
real 253m59.331s
user 0m6.061s
sys 5m4.019s
Tak więc trzy miliony plików usunięto w nieco ponad cztery godziny.
rm -rfv | pv -l >/dev/null
. pv powinno być dostępne w repozytorium EPEL .Odpowiedzi:
Opcja
data=writeback
montowania zasługuje na wypróbowanie, aby zapobiec kronikowaniu systemu plików. Należy to zrobić tylko w czasie usuwania, istnieje jednak ryzyko, że serwer zostanie zamknięty lub ponownie uruchomiony podczas operacji usuwania.Według tej strony ,
Opcja jest ustawiana w
fstab
lub podczas operacji montowania, zastępującdata=ordered
jądata=writeback
. System plików zawierający pliki do usunięcia należy zamontować ponownie.źródło
commit
opcji : „Ta domyślna wartość (lub dowolna niska wartość) zaszkodzi wydajności, ale jest dobra dla bezpieczeństwa danych. Ustawienie jej na 0 będzie miało taki sam efekt, jak pozostawienie jej na wartości domyślnej (5 sekund Ustawienie bardzo dużych wartości poprawi wydajność ".data=writeback
nadal zapisuje metadane przed zapisaniem ich w głównym systemie plików. Jak rozumiem, po prostu nie wymusza porządkowania między takimi rzeczami, jak pisanie mapy zasięgu i zapisywanie danych w tych zakresach. Być może istnieją też inne ograniczenia związane z porządkowaniem, które również się rozluźniają, jeśli zauważysz, że zyskujesz na tym. Oczywiście montaż bez dziennika może być jeszcze lepszą wydajnością. (Może to pozwolić na zmiany metadanych w pamięci RAM bez konieczności posiadania czegokolwiek na dysku przed zakończeniem operacji unlink).Chociaż główną przyczyną tego problemu jest wydajność ext3 w milionach plików, faktyczna główna przyczyna tego problemu jest inna.
Gdy trzeba wymienić katalog, wywoływany jest readdir () w katalogu, który daje listę plików. readdir to wywołanie posiksowe, ale używane tutaj prawdziwe wywołanie systemowe Linuksa nazywa się „getdents”. Getdents wyświetlają wpisy do katalogu, wypełniając bufor wpisami.
Problem polega głównie na tym, że readdir () używa stałego rozmiaru bufora 32 KB do pobierania plików. W miarę powiększania się katalogu (rozmiar rośnie wraz z dodawaniem plików) ext3 zwalnia i spowalnia pobieranie wpisów, a dodatkowy rozmiar bufora 32 kb readdir wystarcza tylko na ułamek wpisów w katalogu. Powoduje to, że readdir ciągle się zapętla i wywołuje drogie wywołanie systemowe w kółko.
Na przykład w katalogu testowym, który utworzyłem z ponad 2,6 milionami plików, uruchomienie „ls -1 | wc-l” pokazuje duży wynik strace wielu wywołań systemowych getdent.
Ponadto czas spędzony w tym katalogu był znaczący.
Metodą uczynienia tego bardziej wydajnym procesem jest ręczne wywoływanie getdents przy użyciu znacznie większego bufora. Znacząco poprawia to wydajność.
Teraz nie powinieneś samodzielnie wywoływać getdents, więc nie istnieje interfejs, aby normalnie z niego korzystać (sprawdź stronę get, aby zobaczyć getdents!), Ale możesz go wywoływać ręcznie i sprawić, że wywoływanie wywołań systemowych będzie bardziej wydajne.
To drastycznie zmniejsza czas potrzebny na pobranie tych plików. Napisałem program, który to robi.
Chociaż nie zwalcza to podstawowego problemu podstawowego (wiele plików w systemie plików, który działa słabo). Prawdopodobnie będzie to znacznie, znacznie szybciej niż wiele opublikowanych alternatyw.
Z góry należy usunąć katalog, którego dotyczy problem, i ponownie go później. Katalogi tylko zwiększają swój rozmiar i mogą pozostać słabej wydajności nawet z kilkoma plikami w środku ze względu na rozmiar katalogu.
Edycja: całkiem sporo to wyczyściłem. Dodano opcję pozwalającą na usunięcie w wierszu poleceń w czasie wykonywania i usunęło kilka elementów chodnika, co, szczerze mówiąc, było co najmniej wątpliwe. Wykazano również, że powoduje uszkodzenie pamięci.
Możesz teraz zrobić
dentls --delete /my/path
Nowe wyniki. Na podstawie katalogu z 1,82 milionami plików.
Byłem trochę zaskoczony, że nadal działa tak dobrze!
źródło
[256]
prawdopodobnie powinna być[FILENAME_MAX]
, a druga, mój Linux (2.6.18 == CentOS 5.x) nie wydaje się zawierać wpisu d_type w katalogu dirent (przynajmniej zgodnie z getdents (2)).Czy można wykonać kopię zapasową wszystkich innych plików z tego systemu plików w tymczasowej lokalizacji do przechowywania, sformatować partycję, a następnie przywrócić pliki?
źródło
W ext3 nie ma limitu plików na katalog tylko limit i-węzłów systemu plików (myślę, że istnieje ograniczenie liczby podkatalogów).
Po usunięciu plików nadal możesz mieć problemy.
Gdy katalog zawiera miliony plików, sam wpis katalogu staje się bardzo duży. Wpis katalogu musi zostać przeskanowany dla każdej operacji usuwania, i zajmuje to różną ilość czasu dla każdego pliku, w zależności od tego, gdzie znajduje się jego wpis. Niestety nawet po usunięciu wszystkich plików pozycja katalogu zachowuje swój rozmiar. Tak więc dalsze operacje wymagające skanowania pozycji katalogu nadal potrwają długo, nawet jeśli katalog jest teraz pusty. Jedynym sposobem rozwiązania tego problemu jest zmiana nazwy katalogu, utworzenie nowego ze starą nazwą i przeniesienie pozostałych plików do nowego. Następnie usuń nazwę o zmienionej nazwie.
źródło
Nie porównałem tego, ale ten facet :
źródło
find po prostu nie działał dla mnie, nawet po zmianie parametrów ext3 fs, zgodnie z sugestią użytkowników powyżej. Spożywane zdecydowanie za dużo pamięci. Ten skrypt PHP rozwiązał problem - szybkie, nieznaczne użycie procesora, nieznaczne zużycie pamięci:
Wysłałem raport o błędzie dotyczący tego problemu z find: http://savannah.gnu.org/bugs/?31961
źródło
Ostatnio napotkałem podobny problem i nie byłem w stanie uzyskać
data=writeback
sugestii ring0 do pracy (być może z powodu faktu, że pliki znajdują się na mojej głównej partycji). Badając obejścia, natknąłem się na to:Spowoduje to całkowite wyłączenie kronikowania, niezależnie od
data
opcjimount
. Połączyłem to znoatime
głośnościądir_index
ustawioną i wydawało się, że działa całkiem dobrze. Usuwanie faktycznie się zakończyło bez konieczności jego zabijania, mój system pozostał responsywny, a teraz jest z powrotem gotowy do pracy (z włączonym kronikowaniem) bez żadnych problemów.źródło
Upewnij się, że:
co również powinno trochę przyspieszyć.
źródło
Bardzo powolne polecenie. Próbować:
źródło
strace -r -p <pid of rm>
aby dołączyć do już działającego procesu rm. Następnie możesz zobaczyć, jak szybkounlink
przewijają się połączenia systemowe. (-r
kładzie czas od poprzedniego wywołania systemowego na początku każdej linii.)Czy
dir_index
ustawiony jest system plików? (tune2fs -l | grep dir_index
) Jeśli nie, włącz. Zwykle jest włączony dla nowego RHEL.źródło
Kilka lat temu znalazłem katalog z 16 milionami plików XML w
/
systemie plików. Ze względu na krytyczność serwera użyliśmy następującego polecenia, którego ukończenie zajęło około 30 godzin :Był to stary dysk twardy o prędkości 7200 obr / min i pomimo wąskiego gardła we / wy i skoków procesora stary serwer nadal działał.
źródło
Moją preferowaną opcją jest podejście newfs, już zasugerowane. Podstawowym problemem jest, jak już wspomniano, liniowy skan do usunięcia jest problematyczny.
rm -rf
powinien być prawie optymalny dla lokalnego systemu plików (NFS byłby inny). Ale przy milionach plików, 36 bajtów na nazwę pliku i 4 na i-węzeł (przypuszczenie, nie sprawdza wartości dla ext3), to 40 * milionów, które należy przechowywać w pamięci RAM tylko dla katalogu.Zgadujesz, że przebijasz pamięć podręczną metadanych systemu plików w systemie Linux, tak że bloki dla jednej strony pliku katalogu są usuwane, gdy nadal używasz innej części, tylko po to, aby ponownie trafić na tę stronę pamięci podręcznej, gdy następna plik został usunięty. Strojenie wydajności Linuksa nie jest moim obszarem, ale / proc / sys / {vm, fs} / prawdopodobnie zawiera coś istotnego.
Jeśli możesz sobie pozwolić na przestoje, możesz rozważyć włączenie funkcji dir_index. Przełącza indeks katalogu z liniowego na coś znacznie bardziej optymalnego do usuwania w dużych katalogach (haszowane b-drzewa).
tune2fs -O dir_index ...
następniee2fsck -D
będzie działać. Chociaż jestem pewien, że to pomogłoby, zanim pojawią się problemy, nie wiem, jak działa konwersja (e2fsck z-D
) podczas obsługi istniejącego katalogu v.large. Kopie zapasowe + ssać i widzieć.źródło
/proc/sys/fs/vfs_cache_pressure
może to być wartość do użycia, ale nie wiem, czy sam katalog zalicza się do pamięci podręcznej strony (bo to jest to) lub do pamięci podręcznej i-węzłów (ponieważ pomimo tego, że nie jest i-węzeł, to metadane FS i z tego powodu są tam zawarte). Jak mówię, tuning maszyn wirtualnych Linux nie jest moim obszarem. Graj i zobacz, co pomaga.Oczywiście nie jabłka do jabłek tutaj, ale ustawiłem mały test i wykonałem następujące czynności:
Utworzono 100 000 512-bajtowych plików w katalogu (
dd
i/dev/urandom
w pętli); zapomniałem go zmierzyć, ale utworzenie tych plików zajęło około 15 minut.Wykonano następujące czynności, aby usunąć wspomniane pliki:
ls -1 | wc -l && time find . -type f -delete
To jest pudełko Pentium 4 2.8GHz (chyba kilkaset GB IDE 7200 RPM; EXT3). Jądro 2.6.27.
źródło
rm
jest to bardzo powolne w przypadku dużej liczby plików, stądfind -delete
opcja. Mając znak wieloznaczny w powłoce, rozwinie każdą dopasowaną nazwę pliku i zakładam, że istnieje ograniczony bufor pamięci, abyś mógł zobaczyć, jak to się stanie nieefektywne.Czasami Perl potrafi zdziałać cuda w takich przypadkach. Czy próbowałeś już, czy mały skrypt taki jak ten może przewyższyć bash i podstawowe polecenia powłoki?
Lub inne, może nawet szybsze podejście Perla:
EDYCJA: Właśnie wypróbowałem moje skrypty Perla. Im bardziej gadatliwy, robi coś dobrze. W moim przypadku próbowałem tego na serwerze wirtualnym z 256 MB pamięci RAM i pół milionem plików.
time find /test/directory | xargs rm
wyniki:w porównaniu do
źródło
*(oN)
]Z tego, co pamiętam, usuwanie i-węzłów w systemach plików ext to O (n ^ 2), więc im więcej plików usuniesz, tym szybciej pójdzie reszta.
Był jeden raz, gdy napotkałem podobny problem (chociaż moje szacunki dotyczyły czasu usuwania ~ 7 godzin), w końcu poszła droga sugerowana przez jftuga w pierwszym komentarzu .
źródło
Cóż, to nie jest prawdziwa odpowiedź, ale ...
Czy można przekonwertować system plików na ext4 i sprawdzić, czy coś się zmieni?
źródło
W porządku, zostało to omówione na różne sposoby w pozostałej części wątku, ale myślałem, że wrzucę moje dwa centy. Winowajcą wydajności w twoim przypadku jest prawdopodobnie readdir. Otrzymujesz listę plików, które niekoniecznie są sekwencyjne na dysku, co powoduje dostęp do dysku w dowolnym miejscu po rozłączeniu. Pliki są na tyle małe, że operacja rozłączenia prawdopodobnie nie przeskakuje zbyt wiele, zerując przestrzeń. Jeśli czytasz katalog, a następnie sortujesz według rosnącego i-węzła, prawdopodobnie uzyskasz lepszą wydajność. Więc readdir w ram (sortuj według i-węzła) -> unlink -> profit.
I-węzeł jest tutaj przybliżonym przybliżeniem ... ale na podstawie twojego przypadku użycia może być dość dokładny ...
źródło
Prawdopodobnie wybrałbym kompilator C i zrobiłbym moralny odpowiednik twojego skryptu. Oznacza to, że użyj,
opendir(3)
aby uzyskać uchwyt katalogu, następnie użyj,readdir(3)
aby uzyskać nazwę plików, a następnie zsumować pliki, gdy je rozłączam, i od czasu do czasu drukuj „% d pliki usunięte” (i ewentualnie upływ czasu lub aktualny znacznik czasu).Nie spodziewam się, że będzie zauważalnie szybszy niż wersja skryptu powłoki, po prostu od czasu do czasu muszę wyrywać kompilator, albo dlatego, że nie ma czystego sposobu robienia tego, co chcę z powłoki lub ponieważ chociaż jest to wykonalne w powłoce, w ten sposób jest nieproduktywnie wolne.
źródło
Prawdopodobnie występują problemy z przepisywaniem katalogu. Najpierw spróbuj usunąć najnowsze pliki. Sprawdź opcje montowania, które odłożą zapis na dysk.
Aby wyświetlić pasek postępu, spróbuj uruchomić coś takiego
rm -rv /mystuff 2>&1 | pv -brtl > /dev/null
źródło
Oto jak usuwam miliony plików śledzenia, które mogą czasem gromadzić się na dużym serwerze bazy danych Oracle:
Uważam, że powoduje to dość powolne usuwanie, które ma niewielki wpływ na wydajność serwera, zwykle coś w stylu godziny na milion plików przy „typowej” konfiguracji 10 000 IOPS.
Często zajmie to kilka minut, zanim zostaną zeskanowane katalogi, wygenerowana zostanie początkowa lista plików, a pierwszy plik zostanie usunięty. Odtąd a. jest powtarzane dla każdego usuniętego pliku.
Opóźnienie spowodowane przez echo na terminalu wykazało wystarczające opóźnienie, aby zapobiec znacznemu obciążeniu podczas usuwania.
źródło
find /u* -maxdepth 3 -mindepth 3 -type d -path '*/app/*' -name diag -print0 | xargs -0I = find = -mindepth 4 -maxdepth 4 -type d -name 'trace' -print0 | xargs -0I = find = -mindepth 1 -maxdepth 1 -name '*.tr'
? Dodaj-delete
do ostatniego, aby faktycznie usunąć rzeczy; jak napisano, po prostu wyszczególnia, co by usunął. Zauważ, że jest to zoptymalizowane pod kątem okoliczności, w których masz wiele nieciekawych rzeczy w pobliskich katalogach; jeśli tak nie jest, możesz znacznie uprościć logikę.rm
zdarzeniem), więc masz stosunkowo wydajne operacje we / wy podczas uruchamiania, a następnie bolesne,rm
niedziałające które prawdopodobnie nie powodują dużej ilościscandir
operacji we / wy, ale wymagają wielokrotnego przeglądania katalogu (nie powodując operacji we / wy, ponieważ zostały one już załadowane do pamięci podręcznej bloków; patrz takżevfs_cache_pressure
). Jeśli chcesz zwolnić,ionice
jest to opcja, ale prawdopodobnie użyłbym sekund ułamkowychsleep
.find /u*/app/*/diag -path '*/trace/*.tr' -execdir rm {} +
uruchamiałby jedenrm
na katalog, więc miałbyś mniej obciążenia procesora. Tak długo, jak masz mnóstwo czasu procesora na oszczędzanie, dławienie dysku IO przez rozwiązywanie całegorm
procesu dla każdejunlink
pracy, tak myślę, ale jest to brzydkie. perl ze snem na unlink byłby ładniejszy, gdyby spanie międzyrm
całymi katalogami naraz było zbyt duże (-execdir sh -c ...
może)Możesz użyć funkcji równoległości „xargs”:
źródło
źródło
tak naprawdę ten jest trochę lepszy, jeśli używana powłoka rozszerza linię poleceń:
źródło