Wie kann ich PHP-Skripten Zeitlimit setzen, während ich auf lange laufende MySQL-Abfragen warte?

8

Ich habe eine PHP-Site, auf der ziemlich viele Datenbankabfragen laufen. Bei bestimmten Kombinationen von Parametern können diese Abfragen für eine lange Zeit ausgeführt werden und eine hässliche Timeout-Nachricht auslösen. Ich möchte dies durch eine nette Timeout-Nachricht ersetzen, die dem Rest meines Site-Stils entspricht.

Antizipieren Sie die üblichen Antworten auf diese Art von Fragen:

  1. "Optimieren Sie Ihre Abfragen, damit sie nicht so lange laufen" - Ich protokolliere lang laufende Abfragen und optimiere sie, aber ich weiß nur über diese, nachdem ein Benutzer betroffen war.

  2. "Erhöhen Sie Ihre PHP-Timeout-Einstellung (z. B. set_time_limit, max_execution_time), damit die lang andauernde Abfrage abgeschlossen werden kann." - Manchmal kann die Abfrage mehrere Minuten lang ausgeführt werden. Ich möchte dem Benutzer mitteilen, dass zuvor ein Problem aufgetreten ist (z. B. nach 30 Sekunden).

  3. "Verwenden Sie register_tick_function, um zu überwachen, wie lange Skripts ausgeführt wurden" - Dies wird nur zwischen Codezeilen in meinem Skript ausgeführt. Während das Skript auf eine Antwort von der Datenbank wartet, wird die Tick-Funktion nicht aufgerufen.

Falls es hilft, wird die Site mit Drupal (mit vielen Anpassungen) erstellt und läuft auf einem dedizierten Linux-Server auf PHP 5.2 mit MySQL 5.

    
Mark B 14.05.2010, 10:37
quelle

3 Antworten

3

Es gibt keine asynchronen mysql-Aufrufe und keinen Spielraum zum Verzweigen von Lightweight-Threads.

Sie können zwar Ihren PHP-Code in zwei Ebenen aufteilen und eine Verbindung zwischen ihnen verwenden, die Sie asynchron aufrufen können. Das Problem bei dieser Vorgehensweise ist jedoch, dass die DB-Ebene versucht, die Abfrage auszuführen, nachdem die oberste Ebene aufgegeben hat Die Ergebnisse zurück - potenziell blockiert das DBMS für andere Benutzer. (Es ist wahrscheinlicher, dass Sie häufiger Anfragen für Seiten mit Zeitlimit erhalten).

Sie haben das gleiche Problem, wenn Sie die Timeout-Behandlung in einen Reverse-Proxy vor dem Webserver schieben.

Der sinnvollste Ort, um das Timeout zu implementieren, ist die Datenbank selbst - aber AFAIK, mysql unterstützt das nicht.

Die nächste Option besteht darin, einen Proxy zwischen der PHP-Datenbank und der Datenbank zu erstellen - dies könnte in sich abgeschlossen sein - und 2 leichte Threads für jede Anfrage generieren (1 für die Abfrage, 2 als Watchdog, um die erste zu beenden, wenn es auch nötig ist) lang) - aber das erfordert nicht nur das Schreiben von Code in einem lnaguage, das leichte Threads unterstützt, sondern auch das Definieren eines Protokolls für die Kommunikation mit PHP.

Wenn Sie jedoch einen anderen Ansatz für das Proxy-Modell wählen - Sie könnten einen separaten PHP-Prozess mit proc_open erstellen und den stdout-Stream als nicht blockierend festlegen - so kann Ihr PHP weiterlaufen und prüfen, ob der Proxy ausgeführt wurde die Abfrage. Wenn das Zeitlimit überschritten wird, kann es als übergeordnetes Element des Proxys das Herunterfahren signalisieren (proc_terminate ()), wodurch die Abfrage in der Datenbank gestoppt werden sollte.

Sicher, es wird eine Menge Entwicklungsarbeit bedeuten.

Es kann sich als viel einfacher erweisen, ein oder mehrere Slave-DBMS einzurichten, um Ihre langsamen Abfragen gegen - möglicherweise mit intelligentem Lastenausgleich - auszuführen. Oder schauen Sie sich andere Möglichkeiten an, um die langsamen Abfragen schneller zu machen - wie vor der Konsolidierung.

HTH

C.

    
symcbean 14.05.2010 12:22
quelle
0

Ist Ihr Server auf APC, Memcache, Boost und Drupal Cache abgestimmt? Das sind alternative Routen, die sehr gut funktionieren.

Sonst, welche Art von Skripten laufen in Drupal, die das verursachen würden? Nur aus Neugier, führen Sie Views und Panels?

    
Kevin 14.05.2010 14:41
quelle
0

Die Handhabung von Verbindungen ist genau das, was Sie brauchen.

Grundsätzlich müssen Sie eine Shutdown-Funktion registrieren, indem Sie register_shutdown_function () verwenden . Diese Funktion wird immer dann aufgerufen, wenn ein Skript beendet wurde, unabhängig davon, ob es erfolgreich abgeschlossen wurde, vom Benutzer abgebrochen wurde (ESC-Taste) oder das Zeitlimit abgelaufen ist.

Diese Shutdown-Funktion kann dann die Funktion connection_status () aufrufen. Wenn connection_status () 2 (TIMEOUT) zurückgibt und die vorherige Seite die problematische Abfrage ausgeführt hat, können Sie den Benutzer auf eine Seite umleiten, die sagt: "Entschuldigung, aber wir haben gerade eine hohe Serverlast." oder was auch immer.

    
Mathew 14.05.2010 11:13
quelle

Tags und Links