PHP-Dateien während Berechnung sperren
lima-city → Forum → Programmiersprachen → PHP, MySQL & .htaccess
anfang
anweisung
aufruf
brauche
code
datei
datenbank
definition
errichtung
http
idee
memory
problem
schnittstelle
speichern
sperre
sperren
tabelle
testen
url
-
Um zu beschreiben, was genau das bedeuten soll, ein kurzer Vergleich: Für MySQL gibt es LOCK TABLE, mit dem ich verhindern kann, dass die selbe Tabelle gleichzeitig von 2 Prozessen gelesen werden kann. Das selbe Prinzip möchte ich nun auch äquivalent für Dateien umsetzen. Genauer gesagt für PHP-Dateien.
Und zwar soll eine Datei immer nur 1 mal gleichzeitig gelesen und nicht parallel mit sich selbst ausgeführt werden können.
Mein Problem ist, dass die Aufrufe die ich meine, auf wenige Millisekunden synchron ankommen und daher selbst ein Datenbankeintrag nicht schnell genug wäre, um den aktuellen Zugriff daran fest zu machen. Daher dachte ich da an eine serverseitige Lösung, evtl mit htaccess. Gibt es da eine Lösung in der Richtung ?
Liebe Grüße
- VampireSilence -
Diskutiere mit und stelle Fragen: Jetzt kostenlos anmelden!
lima-city: Gratis werbefreier Webspace für deine eigene Homepage
-
Das wir nicht trivial gehen. Ich würde wirklich auf das Schreiben in die DB setzten. Insbesondere, wenn du eine MEMORY Tabelle nutzt, sollte das super-schnell gehen ;)
Was du willst ist kaum von PHPs Seite aus zu erledigen, wahrscheinlich müsstest du den Server umschreiben, damit er deinen Ansprüchen genügt. -
Hm, mist das hab ich befürchtet. Aber MEMORY-Tabellen sind durchaus eine Überlegung wert, ich habs bisher immer nur mit MyISAM getestet. Mal schauen wie gut das funktioniert, ich melde mich dann per Edit nochmal.
Edit: Ok, habs nun getestet. Bei der MyISAM waren es knapp 20,35 (+/- 14,74) ms und bei der MEMORY sind es nur noch 6,70 (+/- 1,89) ms. Das bringt mich schonmal ein gewaltiges Stück näher ans Ziel.
Die Tabelle sieht momentan übrigens so aus:
CREATE TABLE `t` (
`a` bit(1) NOT NULL ,
PRIMARY KEY (`a`)
) ENGINE=MEMORY DEFAULT CHARSET=utf8 COLLATE=utf8_bin;
Ich wüsste nicht, wie ich die Datenmenge noch weiter verkleinern sollte, aber wenn ich die Dauer nochmal um 3 ms senken könnte, wäre das perfekt. Bin für gute Ideen immer offen.
Liebe Grüße
- VampireSilence
Beitrag zuletzt geändert: 16.4.2010 15:17:41 von vampiresilence -
Könntest du erklären, wie genau du die t-Tabelle dann nutzt? Also, gibt es immer nur genau einen Eintrag, der 1 oder 0 ist? Wenn ja, dann macht PRIMARY KEY (a) wenig Sinn. (Auch wenn dir das Weglassen kaum die 3 ms geben wird.)
Also, welche Queries nutzt du im Endeffekt? -
Das würde dann so aussehen:
<?php include('db_connect.php'); $query = mysql_query (' SELECT `a` FROM `t` '); if(mysql_num_rows($query) == 0) { $query = mysql_query (' INSERT INTO `t` (`a`) VALUES (1); '); // Code } $query = mysql_query (' DELETE FROM `t` '); ?>
Also alles so simpel und traffic-schonend, wie es nur geht. Ich brauche auch keine Else-Anweisung, da der Code ohnehin keine Ausgabe produzieren würde. Und da ich nur die Info "Existiert / oder Existiert nicht" brauche, hab ich auch kein mysql_fetch_assoc, sondern nur ein mysql_num_rows verwendet. Bin mir nur nicht sicher, ob das include evtl. noch nen paar ms frisst. Das müsste ich dann nochmal testen.
Edit: Ok, das include werd ich besser drin lassen, hab ohne nämlich nen Anstieg auf 9,71 (+/- 5,04) ms.
Liebe Grüße
- VampireSilence
Beitrag zuletzt geändert: 16.4.2010 16:17:05 von vampiresilence -
@vampiresilence
... Also alles so simpel und traffic-schonend, wie es nur geht.
du könntest noch 'select' gegen 'handler' austauschen, auch das bringt speed (nur für myisam o. innodb). handler ist deshalb schneller, weil kein result set geliefert wird. du greift direkt auf die tabellendaten zu! -
Es handelt sich um eine MEMORY Datenbank, die schnellste überhaupt, per Definition ;)
@vampire: Ich denke viel mehr Speed wirst du da auch nicht bekommen. Meine Ideen wären sowas wie den Primary Key entfernen. Oder das include() einlagern. (Ich habe echt keinen blassen Schimmer, wie es dadurch langsamer werden könnte.) Vielleicht sogar auf ein mysql_select_db() verzichten (auch wenn ich keine Ahnung habe, welchen Performance-Einfluss das hat.) Noch ne Idee wäre persistente Verbindungen zu nutzen (unterstützt mysql_ das?) Weiterhin könntest du das Tokenizen und Opcodeizing des Codes überspringen, indem zu APC nutzt. Wie viel diese ganzen Sachen bringen, weiß ich nicht, würde es aber durchaus gerne Erfahren :D -
nikic schrieb: Es handelt sich um eine MEMORY Datenbank, die schnellste überhaupt, per Definition ;)
so verrückt das klingen mag, während meiner anfängerzeit muste man sogar machine code ['der schnellste überhaupt, per definition'] (kein irrtum, ich schpreche nicht vom assembler!) optimieren (wir haben z.b. nie mit 3, 5, 7, &c. multipliziert, wir haben was anders gemacht - für tüftler eine nette denkaufgabe), aber das werdet ihr alle hier wohl nicht wissen ;)
warum also doch handler? (quelle):
es gibt eine reihe von gründen zur verwendung der handler-schnittstelle anstelle einer normalen select-anweisung:
* handler ist schneller als select:
o für handler ... open wird ein dediziertes speicher-engine-spezifisches handler-objekt reserviert. das objekt wird bei nachfolgenden handler-anweisungen für diese tabelle wiederverwendet; es muss nicht jeweils neu initialisiert werden.
o der analyseaufwand ist geringer.
o es ist kein zusätzlicher aufwand für den optimierer oder die abfrageüberprüfung erforderlich.
o die tabelle muss nicht zwischen zwei handler-anforderungen gesperrt werden.
o die handler-schnittstelle muss keine konsistente sicht der daten bieten (dirty reads beispielsweise sind zulässig) – die speicher-engine kann also optimierungen verwenden, die select normalerweise nicht zulassen würde.
* bei anwendungen, die eine maschinennahe isam-artige schnittstelle verwenden, erleichtert handler deren portierung auf mysql.
* handler erlaubt ihnen das durchlaufen einer datenbank auf eine weise, die mit select – wenn überhaupt – nur sehr schwierig zu realisieren ist. die handler-schnittstelle ist eine natürlicher anmutende art der datensicht, wenn sie mit anwendungen arbeiten, die eine interaktive benutzeroberfläche für die datenbank bereitstellen.
und ich glaube, er ist erwachsen genug selbst entscheiden zu können - wenn er will ;)
Beitrag zuletzt geändert: 16.4.2010 19:55:03 von czibere -
Ok, hab mir mal eure Vorschläge zu Herzen genommen und bin zu folgenden Ergebnis gekommen:
Nachdem ich den PRIMARY KEY entfernt habe, war ich bei 203,65 (+/- 753.100.517,13) ms, also eine riesen Ungenauigkeit, bei dem sich die Einzelmessungen in nem Bereich zwischen ~5 und ~800 ms beliefen. Die schnellsten Werte waren allerdings immernoch nicht schneller, als die schnellsten der "mit-PK-Werte", daher wird es wohl besser sein, den drin zu lassen.
Das Script ohne mysql_select_db auszuführen hat leider nicht hingehauen, da die Queries nicht ausgeführt werden konnten, selbst dann, wenn ich die Tabelle explizit als `t`.`a` aufgerufen habe.
Beim Handler und APC hab ich noch keine Ergebnisse, da muss ich mich erstmal rein lesen, wie das von Statten zu gehen hat. Das hab ich noch nie genutzt.
Also wies aussieht, bleib ich fürs Erste auf meinen ~6,7 ms hocken. ^^
Liebe Grüße
- VampireSilence -
Das Script ohne mysql_select_db auszuführen hat leider nicht hingehauen, da die Queries nicht ausgeführt werden konnten, selbst dann, wenn ich die Tabelle explizit als `t`.`a` aufgerufen habe.
t.a wäre Ja eher das Attribut a der Relation t. Du musst eher sowas wie db.t nehmen ;)
€dit: Wie schnell wäre eigentlich das ganze über apc_add / apc_fetch zu speichern/erhalten? Hat APC seinen Cache auf dem Hard Drive oder im RAM? (Mein Tipp wäre, dass es solange ins Memory auslagert, wie es über shm_size Platz hat. Für Opcodes + diese eine 1 oder 0 sollte das reichen. Wenn dem wirklich so ist, dann sollte das über apc_ wirklich fetzen, weil a) die Opcodes gecacht werden und b) auch der "Ausführungsstand" im Memory ist (und nicht einen Datenbankoverhead hat.))
€dit2: Es gibt ja auch noch Memcached...
Beitrag zuletzt geändert: 16.4.2010 23:51:25 von nikic -
ich weiß nicht, ob das was ich im folgenden schreibe, sinn macht oder überhaupt realisierbar ist, aber einfach um mal von der geschichte mit der datenbank wegzukommen:
kannst du nicht einfach am anfang des scripts die chmod rechte der datei auf "nur lesen", bzw "keine berechtigung" setzen, und am ende einfach wieder auf den ursprünglichen wert?
also in etwa so
<?php chmod("scriptname.php", 0000); //berechnungen, was auch immer chmod("scriptname.php", 0777); ?>
mfg -
@syberpsace
... chmod("scriptname.php", 0000); ...
kann meine ungeschicklichkeit sein, aber ich habe es nicht zusammengebracht ;o(
das scheint aber zu greifen:
zeit vom start bis zur errichtung der sperre: ~7.3E-5sec.<?php $a = microtime (); $msg = null; if ( $fh = fopen (__FILE__, "r") ) { # mögliche sperren: 'LOCK_EX | LOCK_NB' oder 'LOCK_SH | LOCK_NB' ... if ( !flock ($fh, LOCK_EX | LOCK_NB) ) {exit ("konnte nicht sperren!");} // dauer der errichtung der sperre: $t = (end ($b = explode (" ", microtime ())) + reset ($b)) - (end ($a = explode (" ", $a)) + reset ($a)); // ... flock ($fh, LOCK_UN); $msg = "gesperrt und wieder entsperrt"; } else {$msg = "konnte nicht öffnen!";} exit ("$msg<br />zeit vom start bis zur errichtung der sperre: $t sec."); ?>
etwas gekürzt:
also ganz ohne tests kommt er auf ~3.2E-5 (kannst noch die zeitmessung wegrechnen).<?php $fh = fopen (__FILE__, "r"); $a = microtime (); flock ($fh, 6); $b = microtime (); $t = (end ($b = explode (" ", $b)) + reset ($b)) - (end ($a = explode (" ", $a)) + reset ($a)); exit ("$t"); ?>
allerdings!: file-handle sollte schon offen sein! ich weiß es nicht ob das geht, habe das prob noch nie gehabt.
Beitrag zuletzt geändert: 17.4.2010 6:48:36 von czibere -
nikic schrieb:
Das Script ohne mysql_select_db auszuführen hat leider nicht hingehauen, da die Queries nicht ausgeführt werden konnten, selbst dann, wenn ich die Tabelle explizit als `t`.`a` aufgerufen habe.
t.a wäre Ja eher das Attribut a der Relation t. Du musst eher sowas wie db.t nehmen ;)
Stimmt, war auch Quatsch, was ich geschrieben hatte. Hatte das in der Form `db`.`t` gemacht.
nikic schrieb:
€dit: Wie schnell wäre eigentlich das ganze über apc_add / apc_fetch zu speichern/erhalten? Hat APC seinen Cache auf dem Hard Drive oder im RAM? (Mein Tipp wäre, dass es solange ins Memory auslagert, wie es über shm_size Platz hat. Für Opcodes + diese eine 1 oder 0 sollte das reichen. Wenn dem wirklich so ist, dann sollte das über apc_ wirklich fetzen, weil a) die Opcodes gecacht werden und b) auch der "Ausführungsstand" im Memory ist (und nicht einen Datenbankoverhead hat.))
Das werd ich gleich mal testen, hab mich da gestern schon ein bisschen rein gelesen.
Edit: So, habs grad getestet. Ergebnis:
Fatal error: Call to undefined function apc_fetch() ...
nikic schrieb:
€dit2: Es gibt ja auch noch Memcached...
Die Jungs von www.mysqlperformanceblog.com hatten in ihrem Blog mal nen Artikel, indem aufgezeigt wurde, dass Memcached nur bei großen Datenmengen in geringer Frequenz APC überlegen ist. Für diesen speziellen Fall ist APC also deutlich besser geeignet. (Quelle: http://www.mysqlperformanceblog.com/2006/09/27/apc-or-memcached/)
@Syberspace
Soweit ich weiss, wirkt chmod nur unter den Dateien selbst, da der Aufruf des Benutzers von Webserver delegiert wird und dieser vom System aus seperate Zugriffsrechte besitzt. Ich werd das aber auch mal Testen.
Hier hätte ich nur das Problem, bzw. die Notwendigkeit, dass das Script auf jeden Fall bis zum Ende durchladen muss, auch dann, wenn der Benutzer Clientseitig den Seitenaufbau abbricht. Das lässt sich in der php.ini regeln, ich weiss nur nich mehr, wie der entsprechende Eintrag dazu hieß. Außerdem wäre es auch nicht so dolle, wenn das Script wegen der MAX_EXECUTION_TIME vorzeitig terminiert wird, denn dann kann ich erstmal wieder manuell im Filemanager den chmod korrigieren. Gleiches gilt natürlich auch für APC. Der Vorteil bei chmod ist allerdings, dass ich hier keine Sperrabfrage brauche und das spart denke ich mal einiges an Zeit.
Edit: Das wäre damit auch getestet. Zunächst mal zur Funktionalität an sich: Der Aufruf wurde sauber, mit folgender Nachricht abgewiesen:
Warning: Unknown: failed to open stream: Permission denied in Unknown on line 0 Fatal error: Unknown: Failed opening required '(...)' (include_path='.:/usr/lib/php/') in Unknown on line 0
Fazit: Ich nehme alles zurück und behaupte das Gegenteil.
Aber jetzt kommt der Clou: Nicht nur, dass diese Variante endlich serverseitig abläuft, sie läuft auch noch in 0,54 (+/- 0,02) ms ab. Das ist phenomenal !
Edit2: So wies aussieht, werden die Scripts auch nach nem clientseitigen Abbruch fortgesetzt. Summa Summarum haben wir also die perfekte Variante gefunden ! Danke nochmal an euch alle !
@czibere
Wenn das stimmt, wäre das der Wahnsinn. Ich habs zu Anfang schon mit FileHandles probiert, allerdings so, dass ich in eine Datei die Information geschrieben hatte, ob die Datei gerade benutzt wird, oder nicht. Das war natürlich deutlich langsamer. Werde deine Variante aber auch gleich mal testen.
Liebe Grüße
- VampireSilence
Beitrag zuletzt geändert: 17.4.2010 11:46:13 von vampiresilence -
Diskutiere mit und stelle Fragen: Jetzt kostenlos anmelden!
lima-city: Gratis werbefreier Webspace für deine eigene Homepage