| | | | |
| |||||||
| PHP-Fortgeschrittene Arbeiten mit PHP ohne Einschränkungen |
|
| | LinkBack | Themen-Optionen | Thema bewerten |
| | |
| Benutzer Registriert seit: 17.06.2009
Beiträge: 97
PHP-Kenntnisse: Fortgeschritten ![]() | Hallo, ich beschäftige mich aktuell wieder mal mit meinem Filecache, den ich dazu nutze Abfrageergebnisse der Datenbank auszulagern. Das typische Konstrukt: Code: $h = fopen($filename, 'r'); flock($h, LOCK_EX); // hier atomar glücklich werden flock($h, LOCK_UN); fclose($h); So hieße es bei dem Beispielcode: Code: $h = fopen($filename, 'r'); // hier könnte ein zweiter Prozess ebenfalls mit fopen($filename, 'r'); loslegen flock($h, LOCK_EX); // je nachdem welcher Prozess zuerst locked, bleibt der andere stehen und wartet ab // der letzte Prozess überschreibt die Datei dann noch mal flock($h, LOCK_UN); fclose($h); Daher bedienen sich manche einem Konstrukt, dass auf link() basiert. Allerdings hängt das vom eigenen Bedarf ab, ob das überhaupt Sinn macht. In der Praxis gibt es denke ich mal drei Nutzungsarten: 1.) Daten die geschrieben und wiederrum selbst als Datenquelle zur Aktualisierung genutzt werden (z.B. Counter) 2.) Daten die geschrieben werden, allerdings nur in Abhängigkeit zu einer dritten Quelle aktualisiert werden (z.B. MySQL) 3.) Daten werden nur einmal geschrieben, die erste Speicherung hat Vorrang (z.B. bei einem Suchspiel "Wer findet es zuerst") Bei 2.) ist ein atomarer Prozess eigentlich unwichtig, denn dadurch, dass die Prozesse nacheinander abgearbeitet werden und sich entsprechend einer externen Quelle bedienen, wird am Ende immer der richtige Datensatz gespeichert. Natürlich kann es dabei vorkommen, dass mehrere Prozesse lesen, obwohl nur einer nötig wäre, aber hier muss man klar abwägen, was schneller ist. Mehrere Leseprozesse, wenn die Datensätze aktualisiert werden oder bei jedem Einlesen der Daten einen atomare Prozess ablaufen zu lassen. Bei 3.) sollte man ein tempname() / link() Konstrukt wählen. Jeder folgende Prozess schlägt fehl. Bei 1.) hätte man gerne einen atomaren Prozess, dieser ist aber nicht möglich, zumindest nicht mit einem tempname() / link() Konstrukt. Schließlich wollen wir die Daten einlesen und gleichzeitig aktualisieren und jeder Prozess soll sich möglichst hinten anstellen. Das geht in dem Fall nicht, da hier einfach nur der erste Prozess gewinnt und die letzten ignoriert werden. Um es doch zu schaffen, muss man folgendes machen: Code: <?php
$filename = 'cache/counter.txt';
$hits = 0;
$h = fopen($filename, 'a');
// alle Prozesse reihen sich brav ein und warten, bis der vorherige fertig ist
if (flock($h, LOCK_EX)) {
// erst jetzt lesen wird die Daten
$hits = intval(@file_get_contents($filename)) + 1;
// und überschreiben sie mit dem neuen wert
ftruncate($h, 0);
fwrite($h, $hits);
flock($h, LOCK_UN);
fclose($h);
umask(0000);
chmod($filename, 0644);
}
echo($hits);
?>
Das Script ist nun atomar, allerdings gibt es da einen kleinen Haken, der sich Performance nennt. Es ist nunmal ziemlich langsam bei mehreren verschiedenen Caches parallel, ständig Filehandler zu generieren, die Dateien zu locken und deren Inhalte nur dann zu beziehen, wenn selber gelockt werden konnte. Auch hat man das Problem, dass sich jeder Prozess in die Warteschlange einreiht. Ein Flaschenhals ist hier vorprogrammiert. Also bleibt einem eigentlich gar nichts anderes übrig, als damit zu leben, dass die Prozesse nicht atomar ablaufen. D.h. für unseren Counter folgendes: Code: <?php
$filename = 'cache/counter.txt';
$hits = 0;
$h = fopen($filename, 'a');
// diesmal resultiert flock() sofort false, wenn nicht gelockt werden kann
if (flock($h, LOCK_EX | LOCK_NB)) {
// erst jetzt lesen wird die Daten
$hits = intval(@file_get_contents($filename)) + 1;
// und überschreiben sie mit dem neuen wert
ftruncate($h, 0);
fwrite($h, $hits);
flock($h, LOCK_UN);
fclose($h);
umask(0000);
chmod($filename, 0644);
}
else {
// mit etwas glück, bekommen wir doch noch ein paar daten
$hits = intval(@file_get_contents($filename)) + 1;
fclose($h);
}
echo($hits);
?>
Daher gehen wir weg von flock() und atomar und machen einfach folgendes: Code: if (($hits = @file_get_contents($filename)) === false || $hits{strlen($hits)-1} == '$') {
$hits = $hits ? $hits+1 : 1;
$h = fopen($filename, 'w');
fwrite($h, $hits . '$');
fclose($h);
umask(0000);
chmod($filename, 0644);
}
Die zweite Bedingung ist ein einfacher Trick, der gleichzeitig den Datensatz validiert. Da wir der Zahl ein Dollarzeichen als Prüfwert anhängen (Wichtig: Der muss am Ende stehen, da Dateien von vorne nach hinten geschrieben werden), wissen wir bei Vorhandensein, dass die Datei weder leer, noch gerade beschrieben wird. Nun kommen wir zu 2.): Wenn wir die Daten nicht jedesmal aktualisieren, sondern hauptsächlich lesen, bietet sich das zuvor genannte Konstrukt perfekt an: Code: if (($data = @file_get_contents($filename)) === false || ($data = @unserialize($data)) === false) {
// read data (db query...)
$data = array(
'id' => 1111,
0 => 'Lorem ipsum dolor sit amet,',
1 => 'Lorem ipsum amet,',
2 => 'Lorem ipsum',
);
$h = @fopen($filename, 'w');
@fwrite($h, serialize($data));
@fclose($h);
@umask(0000);
@chmod($filename, 0644);
}
Zuletzt möchte ich auf eine Aktualisierung per Interval eingehen: Code: if (($data = @file_get_contents($filename)) === false || ($data = @unserialize($data)) === false || filemtime($filename) + 86400 < time()) {
filemtime() liest hier die Dateizeit aus, allerdings ist filemtime() ziemlich langsam und deswegen sollte man wie folgt vorgehen: Code: if (($data = @file_get_contents($filename)) === false || ($data = @unserialize($data)) === false || (mt_rand(0, 100) == 100 && filemtime($filename) + 86400 < $time())) {
Hier sind verschiedene Varianten denkbar. Bei meiner Template-Klasse arbeite ich z.B. mit einem Cookie, dass nur ich als Designer übertrage. Alle anderen Nutzer lösen filemtime() gar nicht erst aus. Bei einem Intervall, wo die Daten möglichst um 00:00 zur Verfügung stehen (wie z.B. Geburtstage), sollte man mit mktime() arbeiten und einen "von bis"-Bereich hinterlegen. Ich hoffe ein paar konnten mit meinem Beitrag etwas anfangen Hier übrigens die Benchmarks zu den verschiedenen Varianten: Benchmark: $gentime vs. filemtime (Filecache) - Forum: PHP Gruß
__________________ meine PHP Scripte |
| | |
| | |
| PHP Code Flüsterer Registriert seit: 21.08.2005 Beiträge: 4682 PHP-Kenntnisse: Fortgeschritten | |
| | ||
| Moderator und Wett-König | Zitat:
__________________ Viele Grüße, Dr.E. ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 1. Think about software design before you start to write code! 2. Discuss and review it together with experts! 3. Choose good tools (-> Adventure PHP Framework (APF))! 4. Write clean and reusable software only! ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | |
| | |
| | |
| Benutzer Registriert seit: 17.06.2009
Beiträge: 97
PHP-Kenntnisse: Fortgeschritten ![]() | Jo das steht in dem ersten Link, den ich gepostet habe, daher bin ich davon ausgegangen Bei flock() / tempname() funktioniert es nur in der gleichen Partition. Bei meinem Script ist das allerdings egal, da es sich nicht atomar bewegt und es auch nicht braucht (aus den zuvor genannten Gründen). Ich denke auch, dass ein atomarer Zugriff so gut wie nie gebraucht wird. Zumindest habe ich noch kein Projekt erlebt, wo das wichtig gewesen wäre, außer bei einem Such-und-Find-Spiel, wo der erste Besucher als Gewinner ermittelt werden sollte. Aber selbst das kann man bei Clustern gut in den Griff kriegen, wenn man parallel eine Datei mit einem unique-Namen speichert, die die microtime() des Besuchers und seine ID enthält. Falls dann mehrere Dateien vorhanden sein sollten, kann man immer noch die auslesen, die die ältesten microtime() enthält.
__________________ meine PHP Scripte |
| | |
| | |||
| Moderator und Wett-König | Zitat:
Zitat:
__________________ Viele Grüße, Dr.E. ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 1. Think about software design before you start to write code! 2. Discuss and review it together with experts! 3. Choose good tools (-> Adventure PHP Framework (APF))! 4. Write clean and reusable software only! ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | ||
| | |
| | |
| Benutzer Registriert seit: 17.06.2009
Beiträge: 97
PHP-Kenntnisse: Fortgeschritten ![]() | Welchen nicht exklusiven Lock meinst Du? Meine Idee enthält keinen Lock.
__________________ meine PHP Scripte |
| | |
| | ||
| Moderator und Wett-König | Zitat:
FAQ/GeneralQuestions - Cluster Wiki und im Speziellen GFS an. Hier findest du unter dem Stichwort DLM einiges.
__________________ Viele Grüße, Dr.E. ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 1. Think about software design before you start to write code! 2. Discuss and review it together with experts! 3. Choose good tools (-> Adventure PHP Framework (APF))! 4. Write clean and reusable software only! ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | |
| | |
| | ||
| Erfahrener Benutzer Registriert seit: 16.07.2005
Beiträge: 1.007
PHP-Kenntnisse: Fortgeschritten ![]() | Zitat:
| |
| | |
|
| Themen-Optionen | |
| Thema bewerten | |
|
|
Ähnliche Themen | ||||
| Thema | Autor | Forum | Antworten | Letzter Beitrag |
| Race Condition Problem (INSERT -> SELECT) | R4v3r | Datenbanken | 5 | 11.08.2009 12:58 |
| Neue Versionen von Thunderbird, SeaMonkey und Flock | PHP Tipps 2007 | 0 | 03.08.2007 12:06 | |
| flock() | brian johnson | PHP-Fortgeschrittene | 7 | 27.12.2006 17:46 |
| Besucher kamen über folgende Suchanfragen bei Google auf diese Seite |
| php atomar, php flock include, php flock, php race condition, php flock race condition, atomarer prozess, race condition php, parallele prozesse php, flock php, php race conditions, race conditions php, php flock hängt, php flock atomar, php datei exklusiv erzeugen atomar, file_put_contents php lock_ex, php.de flock atomar, was bedeutet atomar php, flock chmod php, php txt racing conditions, flock race condition |