» flock() Alternative mkdir() optimiert

include(), MySQL-Cache und Race ConditionsNeuen Thread eröffnenNeue Antwort erstellenAlternative zu MySQLDumper und phpMyAdmin
AutorNachricht
Administrator 

Name: Marc
Geschlecht:
Anmeldedatum: 28.08.2004
Beiträge: 52420
Wohnort: Lohmar


Meine eBay-Auktionen:
28.10.2011, 09:40
zitieren

Hi,

nach diversen Tests kann ich sagen, dass flock() ein Performancekiller ist.

Nicht weil der Befehl an sich langsam ausgeführt wird, sondern weil es schnell zu Staus kommt, wenn hunderte verschiedene Prozesse parallel einen flock() ausführen.

Erstmal zu unserer Testumgebung:
Wir haben eine Community mit ca. 100.000 Impressionen pro Tag (Du bist gerade hier ;)). Um die Datenbank zu entlasten, nehmen wir die häufigsten Datenbankabfragen, die sich so gut wie nie ändern und hinterlegen sie in einem Cache. Aktuell nutzen wir dazu einen filecache.

Das Ergebnis der Abfrage kommt also in eine Datei:
function mkfile($filename, $data='', $chmod=0644) {
$h = @fopen($filename, 'w');
@fwrite($h, $data);
@fclose($h);
@umask(0000);
@chmod($filename, $chmod);
return $data;
}

Nun stellt man sich vor, dass sich das Abfrageergebnis ändert. Der Cache muss also aktualisiert werden. Jetzt kommt das Problem. Innerhalb von einer Sekunde können theoretisch mehrere hundert Besucher den Cache neu erstellen. Umso mehr Daten in der Cachedatei liegen, umso häufiger resultieren parallele Schreibvorgänge.

Es gibt nun zwei Lösungsansätze für dieses Problem:
  1. der Cache wird immer nur von einem Besucher aktualisiert z.B. wenn der Administrator die Seite nutzt.
  2. man sperrt die Datei, solange der Schreibprozess stattfindet
Üblicherweise nutzt man zum Sperren von Dateien die Funktion flock(). Liest man nun die zahlreichen Kommentare bei php.net stellt man fest, dass sich viele über Probleme bei gespiegelten Systemen bzw. Timeoutprobleme äußern:
http://php.net/manual/de/function.flock.php

Daraus resultieren verschiedene Lösungsansätze mit LOCK_NB und sogar $wouldblock:
function mkfile($filename, $data='', $chmod=0644) {
if (($h = @fopen($filename, 'w')) && flock($h, LOCK_EX | LOCK_NB, $wouldblock) && !$wouldblock) {
@fwrite($h, $data);
@fclose($h);
@umask(0000);
@chmod($filename, $chmod);
flock($h, LOCK_UN);
return $data;
}
}

Allerdings haben unsere Tests ergeben, dass auch das mangelhaft ist. Denn obwohl LOCK_NB angeblich ein Timeout verhindert, so haben wir festgestellt, dass das nicht der Fall ist. Und manchmal ist der Prozess kaum aufzuhalten. So als würden hunderte Dateihandles im Raum schweben und auf alle muss noch ein flock() angewendet werden. Soll heißen, dass der PHP Prozess den Server sogar zum Absturz gebracht hätte, wenn wir das Script nicht gekickt hätten.

Aber in den Kommentaren war auch ein Hinweis auf mkdir() bzw. ich fand dazu diesen Thread bei stackoverflow:
http://stackoverflow.com/questions/6967553/php-flock-alternative

Auch das habe ich dann natürlich getestet. Allerdings war auch hier das Ergebnis ernüchternd. Scheinbar wurde auch hier hundertfach parallel mkdir() ausgeführt und die Abarbeitung dessen führte ebenfalls zum Timeout.

Aber das Problem konnte ich schnell lösen, in dem ich mit file_exists() zuerst prüfte, ob mkdir() etwas geschrieben hat. Und damit kommen wir zu meiner finalen Empfehlung:
function mkfile($filename, $data='', $chmod=0644, $flock=false) {
if (($h = @fopen($filename, 'w')) && (!$flock || (!file_exists($filename . '_lock') && @mkdir($filename . '_lock')))) {
@fwrite($h, $data);
@fclose($h);
@umask(0000);
@chmod($filename, $chmod);
if ($flock) rmdir($filename . '_lock');
return $data;
}
return false;
}

Wie man sieht kann man $flock optional einsetzen.

Gruß



Verfasst am: 07.09.2014, 15:52
zitieren

Ich möchte heute verifizieren, ob mkdir() wirklich atomar ist. Dafür habe ich den folgenden Testcode erstellt:
error_reporting(-1);
set_error_handler(function($errno, $errstr, $errfile, $errline, array $errcontext) {
// error was suppressed with the @-operator
if (0 === error_reporting()) {
return false;
}
throw new ErrorException($errstr, 0, $errno, $errfile, $errline);
});
try {
$filename = 'cache3/atomic6.txt';
if (!file_exists($filename . '_lock')) {
if (mkdir($filename . '_lock')) {
if ($h = fopen($filename, 'w')) {
// $wouldblock makes only sense for flock() with LOCK_NB flag: http://stackoverflow.com/a/24823415/318765
// LOCK_NB is not supported in windows (our machine uses debian)
if (flock($h, LOCK_EX | LOCK_NB, $wouldblock)) {
if (!$wouldblock) {
// used from file_rand()
$filesize = 102400;
if ($filesize > 1024) {
for ($i = 0; $i < floor($filesize / 1024); $i++) {
fwrite($h, bin2hex(openssl_random_pseudo_bytes(511)) . "\r\n");
}
$filesize = $filesize - (1024 * $i);
}
$mod = $filesize % 2;
fwrite($h, bin2hex(openssl_random_pseudo_bytes(($filesize - $mod) / 2)));
if ($mod) {
fwrite($h, substr(uniqid(), 0, 1));
}
flock($h, LOCK_UN);
fclose($h);
umask(0000);
chmod($filename, 0644);
}
else {
trigger_error('$wouldblock is true = mkdir() is not atomic');
}
}
else {
trigger_error('flock() failed');
}
if (!rmdir($filename . '_lock')) {
trigger_error('rmdir() failed');
}
}
else {
trigger_error('fopen() failed');
}
}
else {
trigger_error('mkdir() failed');
}
}
}
catch (ErrorException $e) {
mail('webmaster@example.org', 'mkdir() test6 failed', print_r($e, true));
}

Wenn mkdir() atomar sein sollte, dann darf "$wouldblock" niemals "true" sein. $wouldblock wird von flock() nur dann auf true gesetzt, wenn eine Datei bereits blockiert ist und damit nicht noch mal exklusiv fürs Schreiben gelockt (LOCK_EX) werden kann.

Der Test erfolgt auf einem Linux-Server (Debian). Die Ergebnisse können auf einem Windows-Server durchaus anders aussehen. Wer es mit Windows testen möchte, muss in jedem Fall auf LOCK_NB und $wouldblock verzichten (wird dort nicht unterstützt). Ohne LOCK_NB kann man der Rückgabe von flock() vertrauen. Allerdings sollte man hier den Test beobachten und das User-Feedback überwachen. Denn es kann gut sein, dass mkdir() unter Windows nicht atomar ist und dann viele User in einem Timeout hängen bleiben (LOCK_EX-Anfragen stauen sich dann an).

file_rand() ist übrigens die Funktion:
http://www.programmierer-forum.de/eine-datei-mit-zufaelligem-inhalt-generieren-t339139.htm

 3x  bearbeitet

Verfasst am: 08.09.2014, 14:22
zitieren

Ich bekomme häufig Meldungen, dass mkdir() fehlschlägt. D.h. file_exists() alleine wäre schon mal anfällig für Race-Conditions.

Deswegen lasse ich nun mkdir() keine Fehlermeldungen mehr auswerfen:
$GLOBALS['webmaster_email'] = 'mail@example.org';
$filename = 'atomic.txt';

register_shutdown_function(function(){
$error = error_get_last();
if ($error && $error["type"] == E_ERROR) {
mail($GLOBALS['webmaster_email'], 'Fatal error', print_r($error, true));
}
});
error_reporting(-1);
set_error_handler(function($errno, $errstr, $errfile, $errline, array $errcontext) {
// error was suppressed with the @-operator
if (0 === error_reporting()) {
return false;
}
mail($GLOBALS['webmaster_email'], print_r($errstr, true), print_r($errno, true) . print_r($errfile, true) . print_r($errline, true) . print_r($errcontext, true));
if ($errno == E_USER_ERROR) {
exit;
}
return true;
});

if (!file_exists($filename . '_lock')) {
if (@mkdir($filename . '_lock')) {
if ($h = fopen($filename, 'w')) {
// $wouldblock makes only sense for flock() with LOCK_NB flag: http://stackoverflow.com/a/24823415/318765
// LOCK_NB is not supported in windows (our machine uses debian)
if (flock($h, LOCK_EX | LOCK_NB, $wouldblock)) {
if (!$wouldblock) {
// used from file_rand()
$filesize = 102400;
if ($filesize > 1024) {
for ($i = 0; $i < floor($filesize / 1024); $i++) {
fwrite($h, bin2hex(openssl_random_pseudo_bytes(511)) . "\r\n");
}
$filesize = $filesize - (1024 * $i);
}
$mod = $filesize % 2;
fwrite($h, bin2hex(openssl_random_pseudo_bytes(($filesize - $mod) / 2)));
if ($mod) {
fwrite($h, substr(uniqid(), 0, 1));
}
flock($h, LOCK_UN);
fclose($h);
umask(0000);
chmod($filename, 0644);
}
else {
trigger_error('$wouldblock is true = mkdir() is not atomic');
}
}
else {
trigger_error('flock() failed');
}
if (!rmdir($filename . '_lock')) {
trigger_error('rmdir() failed');
}
}
else {
trigger_error('fopen() failed');
}
}
else {
//trigger_error('mkdir() failed');
}
}

 5x  bearbeitet

Verfasst am: 19.09.2014, 09:28
zitieren

Der Test ist abgeschlossen. Es gab keinerlei Race-Conditions. D.h. mkdir() ist atomar und nicht anfällig für Race Conditions. Allerdings braucht man zwingend file_exists(), damit man keinen Flaschenhals bei vielen parallelen Anfragen auslöst.

Das Testergebnis muss nicht für alle Systeme gelten wie z.B. gespiegelte Systeme oder auch Windows. In dem Fall sollte man den oben genannten Code auf seiner Seite einbauen und selber testen. Einfach die E-Mailadresse und den $filename auf die eigenen Bedürfnisse anpassen.
pn email
Gast 
19.09.2014, 09:28
zitieren

Mach mit!

Wenn Dir die Beiträge zum Thread "flock() Alternative mkdir() optimiert" gefallen haben oder Du noch Fragen hast oder Ergänzungen machen möchtest, solltest Du Dich gleich bei uns anmelden:



Registrierte Mitglieder genießen die folgenden Vorteile:
✔ kostenlose Mitgliedschaft
keine Werbung
✔ direkter Austausch mit Gleichgesinnten
✔ neue Fragen stellen oder Diskussionen starten
✔ schnelle Hilfe bei Problemen
✔ Bilder und Videos hochladen
✔ und vieles mehr...


Neuen Thread eröffnenNeue Antwort erstellen
Ähnliche BeiträgeRe:
Letzter Beitrag
mktree / mkdirs - oder mkdir rekursiv
Die folgende Funktion erstellt mehrere Verzeichnisse. Hilfreich in PHP4, wo rekursives mkdir() nicht verfügbar ist. function mktree($path, $mode=0777) { $dirs = explode('/', $path); unset($dirs[...
[PHP]von mgutt
0
1.762
24.02.2008, 16:51
mgutt
Mehrere Datenbankabfragen optimiert
Ich hatte ja heute mit Performance-Problemen zu kämpfen: https://www.maxrev.de/server-monitoring-ueber-linux-was-sagen-mir-die-werte-t352069.htm Beziehungsweise stellte sich schlussendlich heraus, dass einfach nur ein Backup lief und gar keine Probleme...
von mgutt
3
162
20.04.2015, 05:33
&weida?
2video.de wurde optimiert
Ab sofort akzeptiert www.2video.de nur noch Links, die auf HTML-Seiten verweisen. Damit werden fehlerhafte Links abgewiesen und beeinflussen so nicht mehr die allgemeine Performance von 2video.de. Weiterhin wurde ein Fehler im Eingabe-Formular...
von mgutt
3
2.305
22.12.2008, 13:53
mgutt
Webalizer Tuning - Wie man die Auswertung optimiert
Der Webalizer wird häufig unterschätzt, weil er angeblich "falsche" Daten auswirft. Fakt ist aber, dass der Webalizer in der Standardeinstellung einfach nur alles auswertet, was er in den Logfiles findet und das macht er richtig. Gerne wird mit...
[Allgemein]von mgutt
1
32.076
12.02.2009, 18:00
mgutt
Ladezeit beim Bearbeiten von Beiträgen optimiert
Wenn man seinen Beitrag bearbeiten möchte, dann dauerte das Laden des Formulars mit unter bis zu 60 Sekunden. Natürlich hat sich keiner von Euch darüber beschwert :suspekt: Jedenfalls habe ich das behoben. Lädt jetzt wieder sofort. Es fehlte einfach nur...
von mgutt
0
130
03.02.2017, 09:13
mgutt
Automatische Bilderverkleinerung beim Anschauen optimiert
Alle Bilder in Beiträgen werden bekanntlich verkleinert, wenn diese größer sind als die aktuell eingesetzte Bildschirmauflösung. Bisher galt das nur für fremdgehostete Bilder. Das wurde jetzt auch auf Bilder erweitert, die bei uns hochgeladen wurden....
von mgutt
22
2.005
20.04.2010, 10:12
mgutt
Mehr Funktionalität: Bedienung weiter optimiert
Ebenfalls modifiziert wurde die Mittelkonsole: Sie nimmt nun die Digitaluhr auf und bietet einen vergrößerten und leichter erreichbaren Warnblickschalter. Im unteren Bereich findet sich jetzt - ebenfalls leichter erreichbar - der Zigarettenanzünder. Ein...
von mgutt
0
553
21.10.2007, 19:01
mgutt
Bilstein 10 Härtestufen Fahrwerk muss eingestellt und optimiert werden.Weis jmd wo(berlin)
Vorne Rechts knackt es permanent egal ob beim gerade aus fahren oder abbiegen werkstatt sagt fahrwerk gibts noch andere gründe ? Spurstange wie kopf ist in ordnung...
von civicberlin
2
305
07.05.2012, 10:07
civicberlin
ASB vom MC1 als Alternative zur y8?!
Ist wohl baugleich, aber aus Kunststoff. Ergo leichter. Bringt sie noch andere Vorteile? Hat wer Erfahrungen sammeln...
von Asimo1980
4
241
02.03.2019, 09:17
Blues
MB alternative zu EJ6 ???
Also ich bin ja schon seit ewig auf der suche nach einen ej6, aber das passende zu finden ist echt verdammt schwer und ich verliere langsam die lust. Ich habe jetzt auch mal nach anderen modelen gesucht und habe da einen MB (1 glaube ich) gefunden. Der...
von Godsmack
23
1.057
13.04.2008, 12:43
civic_mc2
© 2004 - 2024 www.programmierer-forum.de | Communities | Impressum |