Der Senario:
Sie haben eine Datei mit einer Zeichenfolge (durchschnittlicher Satzwert) in jeder Zeile. Nehmen wir an, diese Datei hat eine Größe von 1 MB (Tausende von Zeilen).
Sie haben ein Skript, das die Datei liest, einige der Zeichenfolgen im Dokument ändert (nicht nur einige Zeilen anfügt, sondern auch entfernt und ändert) und dann alle Daten mit den neuen Daten überschreibt.
Die Fragen:
Verfügt PHP, OS oder httpd usw. des Servers bereits über Systeme, um solche Probleme zu stoppen (Lesen / Schreiben nach der Hälfte des Schreibvorgangs)?
Wenn ja, erklären Sie bitte, wie es funktioniert, und geben Sie Beispiele oder Links zu relevanten Dokumentationen.
Wenn nicht, gibt es Dinge, die ich aktivieren oder einrichten kann, z. B. das Sperren einer Datei, bis ein Schreibvorgang abgeschlossen ist, und das Fehlschlagen aller anderen Lese- und / oder Schreibvorgänge, bis das vorherige Skript den Schreibvorgang abgeschlossen hat?
Meine Annahmen und andere Informationen:
Auf dem betreffenden Server werden PHP und Apache oder Lighttpd ausgeführt.
Wenn das Skript von einem Benutzer aufgerufen wird und sich in der Mitte des Schreibens in die Datei befindet und ein anderer Benutzer die Datei genau zu diesem Zeitpunkt liest. Der Benutzer, der es liest, erhält nicht das vollständige Dokument, da es noch nicht geschrieben wurde. (Wenn diese Annahme falsch ist, korrigieren Sie mich bitte)
Ich beschäftige mich nur mit dem Schreiben und Lesen von PHP in eine Textdatei, insbesondere mit den Funktionen "fopen" / "fwrite" und hauptsächlich "file_put_contents". Ich habe mir die Dokumentation "file_put_contents" angesehen, aber weder den Detaillierungsgrad noch eine gute Erklärung dafür gefunden, was das "LOCK_EX" -Flag ist oder tut.
Das Szenario ist ein Beispiel für ein Worst-Case-Szenario, bei dem ich davon ausgehen würde, dass diese Probleme aufgrund der Größe der Datei und der Art und Weise, wie die Daten bearbeitet werden, mit größerer Wahrscheinlichkeit auftreten. Ich möchte mehr über diese Probleme erfahren und möchte oder brauche keine Antworten oder Kommentare wie "benutze MySQL" oder "Warum machst du das?", Weil ich das nicht mache. Ich möchte nur etwas über das Lesen / Schreiben von Dateien lernen mit PHP und scheinen nicht an den richtigen Stellen / in der richtigen Dokumentation zu suchen und ja, ich verstehe, dass PHP nicht die perfekte Sprache ist, um auf diese Weise mit Dateien zu arbeiten.
quelle
file_put_contents()
ist nur ein Wrapper für denfopen()/fwrite()
Tanz,LOCKEX
macht das gleiche, als ob Sie anrufen würdenflock($handle, LOCKEX)
.Antworten:
1) Nein 3) Nein
Es gibt mehrere Probleme mit dem ursprünglich vorgeschlagenen Ansatz:
Erstens ist auf einigen UNIX-ähnlichen Systemen wie Linux möglicherweise keine Sperrunterstützung implementiert. Das Betriebssystem sperrt Dateien standardmäßig nicht. Ich habe gesehen, dass die Syscalls NOP (No-Operation) sind, aber das ist ein paar Jahre her. Sie müssen also überprüfen, ob eine von Ihrer Instanz der Anwendung gesetzte Sperre von einer anderen Instanz respektiert wird. (dh 2 gleichzeitige Besucher). Wenn die Sperre immer noch nicht implementiert ist [sehr wahrscheinlich], können Sie diese Datei mit dem Betriebssystem überschreiben.
Das zeilenweise Lesen großer Dateien ist aus Leistungsgründen nicht möglich. Ich schlage vor, file_get_contents () zu verwenden, um die gesamte Datei in den Speicher zu laden und sie dann zu explodieren (), um die Zeilen zu erhalten. Alternativ können Sie fread () verwenden, um die Datei in Blöcken zu lesen. Ziel ist es, die Anzahl der Leseanrufe zu minimieren.
In Bezug auf das Sperren von Dateien:
LOCK_EX bedeutet eine exklusive Sperre (normalerweise zum Schreiben). Nur ein Prozess kann zu einem bestimmten Zeitpunkt eine exklusive Sperre für eine bestimmte Datei enthalten. LOCK_SH ist eine gemeinsam genutzte Sperre (normalerweise zum Lesen). Mehr als ein Prozess kann zu einem bestimmten Zeitpunkt eine gemeinsam genutzte Sperre für eine bestimmte Datei enthalten. LOCK_UN entsperrt die Datei. Das Entsperren erfolgt automatisch, wenn Sie file_get_contents () http://en.wikipedia.org/wiki/File_locking#In_Unix-like_systems verwenden
Elegante Lösung
PHP unterstützt Datenstromfilter, die zur Verarbeitung von Daten in Dateien oder von anderen Eingaben vorgesehen sind. Möglicherweise möchten Sie einen solchen Filter mithilfe der Standard-API ordnungsgemäß erstellen. http://php.net/manual/en/function.stream-filter-register.php http://php.net/manual/en/filters.php
Alternative Lösung (in 3 Schritten):
Erstellen Sie eine Warteschlange. Anstatt einen Dateinamen zu verarbeiten, verwenden Sie die Datenbank oder einen anderen Mechanismus, um eindeutige Dateinamen irgendwo in ausstehend / und verarbeitet in / verarbeitet zu speichern. Auf diese Weise wird nichts überschrieben. Die Datenbank ist auch nützlich, um zusätzliche Informationen wie Metadaten, zuverlässige Zeitstempel, Verarbeitungsergebnisse und andere zu speichern.
Lesen Sie für Dateien bis zu einigen MB die gesamte Datei in den Speicher und verarbeiten Sie sie dann (file_get_contents () + explode () + foreach ()).
Bei größeren Dateien lesen Sie die Datei in Blöcken (dh 1024 Bytes) und verarbeiten und schreiben Sie jeden Block in Echtzeit als Lesevorgang (achten Sie auf die letzte Zeile, die nicht mit \ n endet. Sie muss im nächsten Stapel verarbeitet werden).
quelle
Ich weiß, dass dies uralt ist, aber für den Fall, dass jemand darauf stößt. IMHO ist der Weg dahin wie folgt:
1) Öffnen Sie die Originaldatei (z. B. original.txt) mit file_get_contents ('original.txt').
2) Nehmen Sie Ihre Änderungen / Bearbeitungen vor.
3) Verwenden Sie file_put_contents ('original.txt.tmp') und schreiben Sie es in eine temporäre Datei original.txt.tmp.
4) Verschieben Sie dann die tmp-Datei in die Originaldatei und ersetzen Sie die Originaldatei. Dazu verwenden Sie die Umbenennung ('original.txt.tmp', 'original.txt').
Vorteile: Während die Datei verarbeitet und in die Datei geschrieben wird, ist sie nicht gesperrt und andere können den alten Inhalt weiterhin lesen. Zumindest unter Linux / Unix-Boxen ist das Umbenennen eine atomare Operation. Unterbrechungen während des Schreibens der Datei berühren nicht die Originaldatei. Erst wenn die Datei vollständig auf die Festplatte geschrieben wurde, wird sie verschoben. Weitere interessante Informationen finden Sie in den Kommentaren zu http://php.net/manual/en/function.rename.php
Bearbeiten, um Kommentare zu adressieren (auch für Kommentare):
/programming/7054844/is-rename-atomic enthält weitere Verweise darauf, was Sie möglicherweise tun müssen, wenn Sie dateisystemübergreifend arbeiten.
Bei der gemeinsam genutzten Sperre für das Lesen bin ich mir nicht sicher, warum dies erforderlich wäre, da in dieser Implementierung nicht direkt in die Datei geschrieben wird. Die Herde von PHP (die verwendet wird, um die Sperre zu erhalten) ist ein wenig, aber unzuverlässig und kann von anderen Prozessen ignoriert werden. Deshalb schlage ich vor, die Umbenennung zu verwenden.
Die Umbenennungsdatei sollte idealerweise eindeutig für den Prozess benannt werden, der das Umbenennen durchführt, um sicherzustellen, dass nicht zwei Prozesse dasselbe tun. Dies verhindert jedoch natürlich nicht, dass dieselbe Datei gleichzeitig von mehr als einer Person bearbeitet wird. Aber zumindest bleibt die Datei intakt (die letzte Bearbeitung gewinnt).
Schritt 3) & 4) würde dann dies werden:
quelle
tempnam
Funktionen verwenden, mit denen eine Datei atomar erstellt und der Dateiname zurückgegeben wird.In der PHP-Dokumentation für file_put_contents () finden Sie in Beispiel 2 die Verwendung für LOCK_EX .
Der LOCK_EX ist eine Konstante mit einem ganzzahligen Wert, der für einige Funktionen bitweise verwendet werden kann .
Es gibt auch eine spezielle Funktion, um das Sperren von Dateien zu steuern: flock () Weise.
quelle
file_get/put_contents
.Ein Problem, von dem Sie nicht erwähnt haben, dass Sie auch vorsichtig sein müssen, sind die Rennbedingungen, bei denen zwei Instanzen Ihres Skripts fast gleichzeitig ausgeführt werden, z. B. diese Reihenfolge:
Wenn Sie also eine große Datei aktualisieren, müssen Sie diese Datei LOCK_EX, bevor Sie sie lesen, und die Sperre erst aufheben, wenn die Schreibvorgänge ausgeführt wurden. In diesem Beispiel wird die zweite Skriptinstanz meines Erachtens ein wenig hängen bleiben, während sie darauf wartet, auf die Datei zuzugreifen. Dies ist jedoch besser als Datenverlust.
quelle