Im letzten Teil meiner Serie zur Programmierung in MySQL geht es um den Event Scheduler. Dabei handelt es sich um eine Art in MySQL integrierten Cronjob-Dienst. Zu bestimmten Zeiten oder in bestimmten Intervallen kann man so beliebige MySQL-Kommandos ausführen und beispielsweise Routinen aufrufen. In diesem Artikel erläutere ich, wie man den Event Scheduler einsetzen kann und wie man ihn bedient.
Überblick über die Artikelserie
- Sinn und Unsinn
- Einrichtung und Vorbereitung
- Die erste Routine
- Grundkonstrukte
- Cursor
- Trigger
- Der Event Scheduler
Anwendungsmöglichkeiten des Event Schedulers
Events sind immer dann nützlich, wenn Aufgaben zyklisch ausgeführt werden sollen. So ist beispielsweise ein Event denkbar, der jede Nacht die Datenbank bereinigt und alte Daten löscht. Zwar ist dies auch mit einem klassischen Cronjob möglich, der eine PHP-Seite aufruft, die wiederum auf die Datenbank zugreift, der entscheidende Unterschied liegt aber auch hier wieder in der Performance.
Während bei dem klassischen Weg drei Komponenten beteiligt sind (Cronjob, PHP, MySQL), arbeitet die MySQL-Datenbank beim Event Scheduler komplett alleine. Die Anweisungen liegen fertig kompiliert in der Datenbank und können um ein Vielfaches schneller abgearbeitet werden, als dies mit PHP und dem Wechsel der Komponenten möglich ist.
Dies mag bei ein wenig Datenbereinigung in der tiefsten Nacht keine große Rolle spielen, wenn aber aufwändigere Berechnungen nötig sind, sehr wohl. Da bekommt man schnell Probleme mit der PHP Script Runtime und dem Cronjob, der die Anfrage abbricht. Auch bei sehr kurzen Wiederholungszyklen ist der Event Scheduler sinnvoller. Bei einem Projekt musste ich mal einen Monitor erstellen, der durchlaufende Messdaten sekundengenau auswertet. Dafür habe ich ein Event erstellt, das ein Mal pro Sekunde läuft. Dies wäre mit einem Cronjob nicht denkbar gewesen.
Voraussetzungen
Der Event Scheduler ist erst seit MySQL 5.1.6 fester Bestandteil von MySQL. Zunächst sollte man also überprüfen, ob man mindestens diese Version installiert hat. Zudem muss die globale Variable event_scheduler auf on gesetzt sein. Der Default-Wert ist off und daher braucht man entweder volle Zugriffsrechte für den Datenbank-Server (SUPER-Privileg) oder man muss seinen Hoster bitten, den Wert umzuschalten. Um Events zu verwalten, braucht man das neu eingeführte EVENT-Recht.
Event erstellen
Die vereinfachte Syntax zur Erstellung eines zyklischen Events, lautet wie folgt:
CREATE EVENT Eventname ON SCHEDULE EVERY interval DO Anweisung;
Der Eventname ist dabei frei wählbar, als interval sind die üblichen Zeitintervalle (1 DAY, 6 HOUR, etc.) erlaubt und die Anweisung eine beliebige SQL-Anweisung (meistens der Aufruf einer Routine). Mit den Schlüsselworten BEGIN und END kann man auch mehrere Anweisungen ausführen lassen. Um den Überblick zu behalten, würde ich aber eher zu einer Routine raten.
Ein Event, um ein Mal am Tag eine Aufräum-Prozedur aufzurufen, könnte zum Beispiel so aussehen:
CREATE EVENT cleanup_event ON SCHEDULE EVERY 1 DAY DO call cleanup();
Möchte man sicherstellen, dass das Event zu einer bestimmten Uhrzeit ausgeführt wird, kann man mit STARTS zusätzlich einen optionalen Startzeitpunkt hinzufügen. Analog dazu kann man die Aufrufe mit ENDS nur bis zu einem bestimmten Zeitpunkt laufen lassen. So sähe der Befehl aus, um das Event immer um 4 Uhr nachts zu starten:
CREATE EVENT cleanup_event ON SCHEDULE EVERY 1 DAY STARTS '2012-08-24 04:00:00' DO call cleanup();
Soll die Anweisung nicht zyklisch, sondern nur zu einem bestimmten Zeitpunkt in der Zukunft aufgerufen werden, sähe eine mögliche Anweisung so aus:
CREATE EVENT cleanup_event ON SCHEDULE AT '2012-08-24 04:00:00' DO call cleanup();
Dies sollten die wichtigsten Möglichkeiten sein. Wie immer gibt es die vollständige Syntax im MySQL-Handbuch.
Events einsehen und bearbeiten
Mit dem folgenden Befehl bekommt man eine Liste mit allen in der Datenbank erstellten Events inklusive der Ausführungszeiten etc.
SHOW EVENTS;
Bestehende Events kann man mit ALTER bearbeiten (abgesehen vom ersten Wort ist die Syntax identisch mit der CREATE-Syntax):
ALTER EVENT Eventname ON SCHEDULE EVERY interval DO Anweisung;
Möchte man ein Event löschen, kann man dies wie üblich mit DROP tun:
DROP EVENT Eventname;
Fazit
Wie auch bei den anderen Möglichkeiten der MySQL-Programmierung wird das Rad durch den Event Scheduler nicht neu erfunden. Die hohe Performance und die geringe Abhängigkeit von anderen Komponenten machen ihn jedoch häufig zu einer praktischen Alternative. Leider gibt es in der MySQL Workbench noch keine Möglichkeit zur bequemen Administration der Events und so muss man sich ein wenig in die Syntax einarbeiten. In Zukunft ist aber damit zu rechnen, dass ein solches Feature kommt.
Hier endet meine Artikelserie zur Programmierung in MySQL. Ich denke, ich habe alle wichtigen Bereiche so weit behandelt, dass der Einstieg in die MySQL-Programmierung für dich geschafft ist. Wenn dir hier noch Informationen fehlen oder du ein spezifisches Problem habt, kannst du gerne einen Kommentar hinterlassen.
Merkblatt
Damit du immer schnell nachgucken kannst wie das mit der MySQL Programmierung nochmal ging, habe ich ein zweiseitiges Merkblatt erstellt. Dort stehen alle Beispiele dieser Serie nochmal drauf. Du kannst das Merkblatt ausdrucken und mit einem Blick dein Gedächtnis auffrischen.
Außerdem kannst du meine regelmäßigen Entwickler Tipps bestellen. Etwa ein Mal pro Monat verschicke ich Tipps zur Arbeit als Entwickler, sowohl zur technischen Seite als auch zur Zusammenarbeit mit Kunden.
Hallo. Erneut bitte ich dich um deinen Rat. Ich bin nun von phpmyAdmin auf die MySQL-Workbench umgestiegen. Und habe immer noch das gleiche Problem. Ich hab den funktionierenden Trigger. Ich habe eine Routine
(DELIMITER $$
CREATE DEFINER=`xx`@`yy` PROCEDURE `new_procedure`()
BEGIN
DECLARE var_timer_id INT;
SELECT Timer_ID INTO var_timer_id FROM timer Where Timer_ID = 12;
IF timer.endtime < now() THEN
UPDATE timer SET timer.Finished = 1 WHERE timer.Timer_ID = var_timer_id LIMIT 1;
END IF;
END )
und ein Event.
(CREATE EVENT `Eventnewprocedureaufrufen` ON SCHEDULE EVERY 1 MINUTE DO Call new_procedure; )
Ich erhalte wieder wie in phpMyAdmin 0 rows affected
Hi Chris,
in der IF-Abfrage ist timer.endtime kein bekannter Ausdruck. Am besten baust du die Bedingung schon ins SELECT ein:
…
SELECT Timer_ID INTO var_timer_id FROM timer Where endtime < now() limit 1; IF var_timer_id IS NOT NULL THEN ...Viele Grüße, Jonas
Eine Frage zu den Event: Gibt es spezielle Einstellungen oder Vorraussetzungen die man haben muss, damit man Events ausführen kann? (in der MySQL Workbench).
Das steht im Absatz Voraussetzungen. MySQL >= 5.1.6, globale Variable event_scheduler auf on und das EVENT Recht.
Hallo,
ein super spannender Artikel. Vielen Dank!!!
Dazu mal eine erweiterte Frage: wäre es möglich direkt über ein MySQL-Event auf eine entferte MS SQL Datenbank zuzugreifen und über eine Abfrage periodisch Daten zu holen und an eine lokale MySQL-Tabelle anzufügen???
Danke Kai. Per MySQL auf eine entfernte MS SQL Datenbank zugreifen ist soweit ich weiß nicht möglich. Da müsstest du eine Instanz dazwischen schalten, die beides kann. Als Webentwickler würde ich da PHP nehmen, geht aber natürlich auch mit C++ und Co.
Wenn du Zugriff auf den MS SQL Server hast, gibt es aber anscheinend eine Möglichkeit es andersrum zu machen: http://www.ideaexcursion.com/2009/02/25/howto-setup-sql-server-linked-server-to-mysql/
Danke – das sieht gut aus!
Hi Jonas,
erst einmal Danke für den Interessanten Artikel.
Wäre schön wenn du auch mal ein Blick auf mein Script werfen könntest.
Es hatte auch schon funktioniert Fehler werden keine gemeldet und der Scheduler hat auch einen Eintrag mit dem Trigger jedoch wird er (nie) ausgeführt.
script
DELIMITER $$
DROP EVENT `reset_fzgstatus`$$
CREATE DEFINER=`root`@`localhost` EVENT `reset_fzgstatus`
ON SCHEDULE EVERY 1 MINUTE STARTS CURRENT_TIMESTAMP
ON COMPLETION NOT PRESERVE ENABLE
DO
BEGIN
UPDATE `fahrzeuge`
SET `status`= ‚Frei‘, `res_ab`=NULL, `bemerkungen`=„
WHERE
(
((`status`=’Reserviert‘)AND (TIMESTAMPDIFF(MINUTE,`fahrzeuge`.`res_ab`, NOW()) >= 30))
OR
((`status`=’Reserviert‘)AND (`fahrzeuge`.`res_ab` = 0 ))
OR
((`status`=’Reserviert‘)AND (`fahrzeuge`.`res_ab` = NULL) )
OR
((`status`=’Frei‘)AND (TIMESTAMPDIFF(MINUTE,`fahrzeuge`.`res_ab`, NOW()) = NULL)AND (`fahrzeuge`.`bemerkungen`NULL) )
);
END$$
DELIMITER ;
Hi Lars,
also prinzipiell bevorzuge ich es, für Events eine Stored Procedure zu erstellen und im Event dann als einzigen Befehl einen CALL zu haben. Dann kann ich bei Problemen die Procedure auch separat aufrufen, und so besser festellen, ob das Problem beim Event selbst liegt, oder bei den Befehlen.
Folgende Dinge fallen mir bei deinem Code auf, nicht alles davon führt zwangsläufig zu einem Fehler. Es kann auch sein, dass WordPress den Code teilweise verunstaltet hat.
– Du gibst bei deinem CREATE Statement sehr viel explizit an. Ich glaube, das sind alles die Default-Werte, aber versuch trotzdem mal meine einfache Syntax aus dem Artikel
– `bemerkungen`=“ : Stell sicher, dass du hier die richtigen Anführungszeichen (Hochkommata) benutzt. Die Backticks sind für Feldnamen reserviert.
– `fahrzeuge`.`res_ab` = NULL : Vergleiche mit NULL sollten immer über IS NULL laufen, siehe http://dev.mysql.com/doc/refman/5.1/de/problems-with-null.html
-`fahrzeuge`.`bemerkungen`NULL : Auch hier fehlt ein IS.
Ich hoffe, ich konnte dir damit helfen.
Schöne Grüße,
Jonas