Bestimmen Sie den Ort der Inode-Verwendung

15

Ich habe kürzlich Munin auf einem Entwicklungs-Webserver installiert, um die Systemnutzung zu verfolgen. Ich habe bemerkt, dass die Inode-Auslastung des Systems um 7-8% pro Tag steigt, obwohl die Festplattenauslastung kaum zugenommen hat. Ich vermute, etwas schreibt eine Menge winziger Dateien, aber ich kann nicht finden, was / wo.

Ich weiß, wie ich die Speicherplatznutzung finde, aber ich kann scheinbar keine Möglichkeit finden, die Inode-Nutzung zusammenzufassen.

Gibt es eine gute Möglichkeit, die Inode-Verwendung nach Verzeichnis zu bestimmen, damit ich die Quelle der Verwendung finden kann?

Dave Forgac
quelle

Antworten:

15

Erwarten Sie nicht, dass es schnell gehen wird ...

cd in ein Verzeichnis, in dem sich vermutlich ein Unterverzeichnis mit vielen Inodes befindet. Wenn dieses Skript sehr viel Zeit in Anspruch nimmt, haben Sie wahrscheinlich herausgefunden, wo Sie im Dateisystem nachsehen müssen. / var ist ein guter Anfang ...

Andernfalls finden Sie das Verzeichnis mit allen Inodes, wenn Sie in das oberste Verzeichnis in diesem Dateisystem wechseln und dieses ausführen und warten, bis es fertig ist.

find . -type d | 
while 
  read line  
do 
  echo "$( find "$line" -maxdepth 1 | wc -l) $line"  
done | 
sort -rn | less

Ich mache mir keine Sorgen über die Kosten für das Sortieren. Ich habe einen Test durchgeführt und das Durchsuchen der unsortierten Ausgabe von 350.000 Verzeichnissen dauerte 8 Sekunden. Der erste Fund dauerte. Die tatsächlichen Kosten sind das Öffnen aller dieser Verzeichnisse in der while-Schleife. (Die Schleife selbst dauert 22 Sekunden). (Die Testdaten wurden in einem Unterverzeichnis mit 350.000 Verzeichnissen ausgeführt, von denen eines eine Million Dateien enthielt, der Rest zwischen 1 und 15 Verzeichnisse).

Verschiedene Leute hatten darauf hingewiesen, dass ls darin nicht großartig ist, weil es die Ausgabe sortiert. Ich hatte echo ausprobiert, aber das ist auch nicht so toll. Jemand anderes hatte darauf hingewiesen, dass stat diese Information (Anzahl der Verzeichniseinträge) angibt, diese aber nicht portierbar ist. Es stellt sich heraus, dass find -maxdepth sehr schnell Verzeichnisse öffnet und .files zählt, also ... hier ist es ... Punkte für alle!

chris
quelle
2
@mike G: Sie haben zu 100% Recht damit, dass dies nicht der schnellste Weg ist, so etwas zu tun. Meiner Meinung nach besteht die richtige Möglichkeit, dies zu optimieren, darin, beim Starten und Beenden des Teils "Verzeichniseinträge zählen" des Skripts zu stderr umzuleiten. Auf diese Weise wird beim Aufrufen eines Verzeichnisses mit einer Million Einträgen "Verzeichnis spool / postfix / maildrop wird verarbeitet" und nicht sofort "erledigt" angezeigt Dateien.
Chris
Ich war auch nicht besorgt über die Kosten des Sortierens, da dies eine einmalige oder zumindest ziemlich seltene Aufgabe ist.
Dave Forgac
7

Wenn es sich bei dem Problem um ein Verzeichnis mit zu vielen Dateien handelt, ist dies eine einfache Lösung:

# Let's find which partition is out of inodes:
$ df -hi
Filesystem            Inodes   IUsed   IFree IUse% Mounted on
/dev/sda3               2.4M    2.4M       0  100% /
...

# Okay, now we know the mount point with no free inodes,
# let's find a directory with too many files:
$ find / -xdev -size +100k -type d

Die Idee dahinter findist, dass die Größe eines Verzeichnisses proportional zur Anzahl der Dateien ist, die sich direkt in diesem Verzeichnis befinden. Hier suchen wir also nach Verzeichnissen mit Tonnen von Dateien.

Wenn Sie keine Zahl erraten möchten und alle verdächtigen Verzeichnisse nach "Größe" sortieren möchten, ist dies ebenfalls ganz einfach:

# Remove the "sort" command if you want incremental output
find / -xdev -size +10k -type d -printf '%s %p\n' | sort -n
Denilson Sá Maia
quelle
6

Grrr, das Kommentieren erfordert 50 Wiederholungen. Diese Antwort ist eigentlich ein Kommentar zu Chris 'Antwort.

Da dem Fragesteller wahrscheinlich nicht alle Verzeichnisse wichtig sind, sondern nur die schlechtesten, ist die Verwendung von sort wahrscheinlich sehr teuer.

find . -type d | 
while 
  read line  
do 
  echo "$(ls "$line" | wc -l) $line"  
done | 
perl -a -ne'next unless $F[0]>=$max; print; $max=$F[0]'  | less

Dies ist nicht so vollständig wie Ihre Version, aber dies bewirkt, dass Zeilen gedruckt werden, wenn sie größer als das vorherige Maximum sind, wodurch die Menge der ausgedruckten Geräusche erheblich reduziert und die Kosten für die Sortierung gespart werden.

Der Nachteil davon ist, wenn Sie 2 sehr große Verzeichnisse haben und das erste zufällig 1 mehr Inode als das zweite hat, werden Sie das zweite nie sehen.

Eine vollständigere Lösung wäre, ein intelligenteres Perl-Skript zu schreiben, das die Top-10-Werte protokolliert und diese am Ende ausgibt. Aber das ist zu lang für eine schnelle Antwort auf einen Serverfehler.

Außerdem können Sie mit einem etwas schlaueren Perl-Skript die while-Schleife überspringen - auf den meisten Plattformen sortiert ls die Ergebnisse, was auch für große Verzeichnisse sehr teuer sein kann. Die Sortierung ls ist hier nicht erforderlich, da uns nur die Zählung am Herzen liegt.

Mike G.
quelle
1
In solchen Situationen sorge ich mich mehr darum, dass klar ist, was ich tue, und nicht so sehr um die Leistung. Ich bin mir ziemlich sicher, dass Sie echo $ line / * | verwenden können wc -w anstelle von ls $ line | wc -l und du vermeidest das ls-Sortierproblem.
Chris
Ich habe gerade einen Test für ein Verzeichnis mit einer Million Dateien durchgeführt und ls hat 22 Sekunden und echo * 12 Sekunden gedauert. (Für den Rekord, Echo * in Shell wird das Arg-Limit nicht erreichen, da Echo in 99% der aktiv genutzten Shells eingebaut ist)
Chris
ls -f sortiert die Ergebnisse nicht. Das Sortieren von Verzeichnisergebnissen führt zu einem häufigen Problem mit NFS und großen Verzeichnissen. Wenn die Zeit zum Lesen und Sortieren des Verzeichnisses (auf dem Server) das NFS-Zeitlimit überschreitet, sind das Verzeichnis und die Unterverzeichnisse unbrauchbar.
mpez0
5

Sie können dieses kleine Snippet verwenden:

find | cut -d/ -f2 | uniq -c | sort -n

Es wird gedruckt, wie viele Dateien und Verzeichnisse sich in jedem der Verzeichnisse im aktuellen Ordner befinden, wobei die größten Straftäter unten stehen. Es wird Ihnen helfen, Verzeichnisse mit vielen Dateien zu finden. ( mehr info )

Rory
quelle
Das hat hervorragend funktioniert.
ptman
3

Dies ist keine direkte Antwort auf Ihre Frage, aber die Suche nach kürzlich geänderten Dateien mit einer geringen Größe mithilfe von find kann Ihre Suche einschränken:

find / -mmin -10 -size -20k
Kyle Brandt
quelle
3
find /path ! -type d | sed 's,/[^/]*$,,' | uniq -c | sort -rn

Es werden keine Dateien gefunden, deren Namen mit einem Punkt beginnen. Die Verwendung von find vermeidet dies. Dadurch wird jede Datei in der Verzeichnisstruktur gefunden, der Basisname am Ende jedes Pfads entfernt und die Häufigkeit gezählt, mit der jeder Verzeichnispfad in der resultierenden Ausgabe angezeigt wird. Möglicherweise müssen Sie das "!" in Anführungszeichen, wenn sich Ihre Shell darüber beschwert.

Inodes können auch von Dateien verwendet werden, die gelöscht wurden, aber von einem laufenden Prozess geöffnet werden. Wenn dieses Munin-Paket ständig laufende Programme enthält, müssen Sie außerdem prüfen, ob ungewöhnlich viele Dateien geöffnet sind.

Kenster
quelle
Die Inodes könnten auch von wirklich tiefen Verzeichnissen übernommen werden, die diese nicht finden. Es gibt eine Reihe von seltsamen Randfällen, aber die häufigste Situation ist ein Verzeichnis voller Dateien mit normalen Namen.
Chris
3

Ich würde dieses brutale Vorgehen erzwingen: Führe eine Tripwire-Prüfung auf dem gesamten Gerät durch, um eine Basislinie zu erhalten, und führe einige Zeit später eine Überprüfung durch.

Geoff Fritz
quelle
Das würde wahrscheinlich eine Milliarde Jahre dauern. Schneller ist es, lsof | auszuführen grep DIR und suche in jedem dieser Verzeichnisse nach vielen neuen Dateien.
Chris
2
Ok, wie wäre es damit: find / | sort> /tmp/find1.txt; find / | sort> /tmp/find2.txt; diff /tmp/find1.txt /tmp/find2.txt
Geoff Fritz
2

(nicht in der Lage zu sein, zu kommentieren, ist wirklich in die Jahre gekommen - das ist für egorgry)

egorgry - ls -i gibt die Inode-NUMMER für einen Eintrag aus, nicht den Inode-COUNT.

Versuchen Sie es mit einer Datei in Ihrem Verzeichnis - Sie werden (wahrscheinlich) eine ebenso hohe Zahl sehen, aber es ist nicht die Anzahl der Inodes, sondern nur die # Inode, auf die Ihr Verzeichniseintrag verweist.

Mike G.
quelle
lol. Ich habe dich gewählt. Danke für die Erklärung. Inode-Nutzung war schon immer verwirrend.
egorgry
danke Jetzt habe ich Angst, dies in einen Kommentar auf Ihrem Knoten umzuwandeln, falls ich das Karma verliere, wenn ich diese Antwort lösche :)
Mike G.
2

Aktualisieren

Ein Einzeiler, der die Inode-Anzahl jedes untergeordneten Elements des angegebenen Verzeichnisses mit den größten Einträgen unten zurückgibt.

find . -mindepth 1 -printf "%p/%i\n" \
  | awk -F/ '{print $2"/"$NF}' | sort -u \
  | cut -d/ -f1 | uniq -c | sort -n

Ursprüngliche Antwort

#!/bin/bash
# Show inode distribution for given directory

dirs=$(find $1 -mindepth 1 -maxdepth 1 -type d)

for dir in $dirs
do
    inode_count=$(find $dir -printf "%i\n" 2> /dev/null | sort -u | wc -l)
    echo "$inode_count $dir"
done

Führen Sie es so aus (vorausgesetzt, das obige Skript befindet sich in einer ausführbaren Datei in Ihrem Arbeitsverzeichnis)

./indist / | sort -n
Raffinesse
quelle
1

Die Inode-Nutzung beträgt ungefähr eine pro Datei oder Verzeichnis, richtig? Dann mach's

find [path] -print | wc -l

um ungefähr zu zählen, wie viele Inodes unter [Pfad] verwendet werden.

pjz
quelle
1

Ich habe versucht, eine effiziente Shell-Pipeline zu schreiben, diese wurde jedoch unhandlich und entweder langsam oder ungenau.

find . -depth -printf '%h\n' | uniq -c | awk '$1>1000'

listet Blattverzeichnisse (und einige andere) mit mehr als 1000 Dateien auf. Also, hier ist ein Perl-Skript, um es sowohl in der Zeit als auch im RAM effizient zu machen. Ausgabe ist wie

«Files-in-subtree» «files-direkt-im-verzeichnis» «verzeichnisname»

So können Sie es einfach mit normalen Werkzeugen wie oben beschrieben massieren und filtern (1) oder awk (1).

#! /usr/bin/perl -w
# Written by Kjetil Torgrim Homme <[email protected]>

use strict;
use File::Find;

my %counted;
my %total;

sub count {
    ++$counted{$File::Find::dir};
}

sub exeunt {
    my $dir = $File::Find::dir;

    # Don't report leaf directories with no files
    return unless $counted{$dir}; 

    my $parent = $dir;
    $parent =~ s!/[^/]*$!!;

    $total{$dir} += $counted{$dir};
    $total{$parent} += $total{$dir} if $parent ne $dir;
    printf("%8d %8d %s\n", $total{$dir}, $counted{$dir}, $dir);
    delete $counted{$dir};
    delete $total{$dir};
}

die "Usage: $0 [DIRECTORY...]\n" if (@ARGV && $ARGV[0] =~ /^-/);
push(@ARGV, ".") unless @ARGV;

finddepth({ wanted => \&count, postprocess => \&exeunt}, @ARGV);

quelle
-1
[gregm@zorak2 /]$ ls -i /home
131191 gregm

Mein Zuhause auf meinem Laptop benutzt 131191 Inodes.

eifrig
quelle
3
ls -i gibt die Inode-NUMMER für einen Eintrag aus, nicht die Inode-COUNT. Versuchen Sie es mit einer Datei in Ihrem Verzeichnis - Sie werden (wahrscheinlich) eine ebenso hohe Zahl sehen, aber es ist nicht die Anzahl der Inodes, sondern nur die # Inode, auf die Ihr Verzeichniseintrag verweist.
egorgry