MichaelR: Kapazität ...

Hallo,

Zuerst ein paar Hintergrundinformationen:
Seit einigen Jahren betreue ich ein Anmeldesystem für ein Institut einer Universität. Jeweils zu Beginn des Semester melden sich hunderte von Studenten über dieses System nach dem Prinzip first-come-first-serve für diverse Veranstaltungen an.
Bei jeder Anmeldung werden bestimmte Sicherheitschecks durchgeführt, ca. eine Handvoll, um festzustellen, dass sich der jeweilige Student auch tatsächlich für den gewählten Kurs anmelden darf/kann.

Bisher hatten wir immer Kapazitätsprobleme zu Beginn der Anmeldephase, da der Server nach meinen Kenntnissen nur 300 Anfragen zeitgleich bearbeiten kann - es aber auf Grund der Studentenzahlen deutlich mehr Anfragen sind (im Schnitt kann man sagen, nach Ablauf der Anmeldephase liegen über 4.000 Anmeldungen zu Kursen und nochmal ca. 1.200 Eintragungen auf einer Warteliste vor).

Sobald wir bisher also eine Hochzeit der Anmeldung durchlebt haben, brach der Server regelmäßig zusammen, d.h. er wurde langsamer und reagierte teilweise minutenlang nicht mehr.

Um diesem Problem endgültig zu begnen, würde ich gerne ein paar Erfahrungen eurerseits mit ähnlich großen Projekten wissen.
Was ließe sich tun, um die Kapazität optimaler zu nutzen und damit die beschriebenen Serverprobleme in den Griff zu bekommen?

Eine Sache, was ich bisher immer wieder versucht habe, ist interne PHP-Anfragen und SQL-Anfragen an die Datenbank zu bündeln, also statt 10 einzelner SQL-Anfragen eben max. 2 etc.

Ansonsten ist das Programm letztlich ziemlich "einfach" gestrickt, aber aufgrund der Masse an Leute, die in kurzer Zeit darauf zugreifen, zwingt es den Server eben in die Knie...

Grüße,
Michael

  1. Hi,

    ein paar Tipps von mir.

    Du solltest zunächst einmal ermitteln, welches Subsystem genau nicht mehr reagiert, bzw. überlastet ist. Es könnte der WebServer sein, der nicht genügend Prozessor/Ram Resourcen für PHP bei x gleichzeitigen Anfragen garantieren kann. Dito für die Datenbank. Ist sowohl DB als auch WebServer physikalisch auf der selben Maschine?

    Es würden imho folgende Schritte zur Behebung durchaus Sinn machen:

    • mehr CPU, mehr RAM für den Server
    • physikalische Trennung von WebServer und Datenbankserver
    • Implementierung von LoadBalancing für den WebServer (Aufteilung der Anfragen auf mehrere WebServer)

    Grüsse
    Frnak

    1. Hallo,

      Du solltest zunächst einmal ermitteln, welches Subsystem genau nicht mehr reagiert, bzw. überlastet ist. Es könnte der WebServer sein, der nicht genügend Prozessor/Ram Resourcen für PHP bei x gleichzeitigen Anfragen garantieren kann. Dito für die Datenbank. Ist sowohl DB als auch WebServer physikalisch auf der selben Maschine?

      Genau hier liegt auch eines der Probleme. Ich hab nur als "User" Zugriff auf den Server (egal ob der Apache oder die SQL-DB). Der Server wird direkt von einer Abteilung der Uni verwaltet.
      Aus meiner Erfahrung der letzten Semester/Jahre weiß ich, dass selbst die Leute von der Uni, die den Server administrieren, mir nicht sagen konnten woran es genau lag, es hieß immer nur der Server (mit PHP/MySQL) ist ausgelastet....

      Es würden imho folgende Schritte zur Behebung durchaus Sinn machen:

      • mehr CPU, mehr RAM für den Server

      Das wäre wünschenswert, scheidet aber aus, weil u. a. ich keinen direkten Zugriff darauf hab.

      • physikalische Trennung von WebServer und Datenbankserver

      Daran hab ich heute auch schon mal gedacht. Ginge es u. U. sogar einen PHP-Server auf einen MySQL-Server auf einer anderen Domain zugreifen zu lassen? Wie mache ich in diesem Fall dem PHP klar, dass es by z. B. mysql_query() den externen DB-Server unter der Adresse www.muster-db.de verwenden soll?

      • Implementierung von LoadBalancing für den WebServer (Aufteilung der Anfragen auf mehrere WebServer)

      Auch eine gute Idee..

      Danke, Grüße,
      Michael

      1. Hallo,

        ich würde deinen Aussagen nach behaupten, dein SLA (Service Level Agreement = Serviceleistungsvereinbarung) mit der Verwaltung des Servers ist suboptimal. Wenn deine Anwendung wichtig (um nicht geschäftskritisch zu sagen) ist, dann solltest du mit der Verwaltung eben eine oder mehrere der genannten Optionen verhandeln:

        • Hardwareupgrade
        • mehrere physikalische Systeme

        Und sie sollten als Verwalter schon in der Lage sein, festzustellen, wo es ~ wenn es mal klemmt.

        Ich nehme auch an, dass bei deinem Problem mehrere Faktoren zusammenspielen.

        Was hindert dich daran mit der ganzen Chose auf einen externen Anbieter auszuweichen, der dir entsprechende Garantien bzw. Leistungen bietet? Sicherlich die Kosten? Ich kenne die Zustände bei euch nicht, aber versuch doch einfach mal etwas mit Nachdruck auf einer Verbesserung der Situation zu bestehen. Es wird doch sicherlich irgendwo im Uni-RZ ein weniger- bis überhauptnicht-ausgelasteten Rechner zu finden sein, den man die dedizierte Rolle DB (mysql) Server aufdrücken könnte.

        Ob du eine entfernte MySQL DB über einen Internet-Hostnamen ansprechen kannst, wird unter anderem davon abhängen ob entsprechende Ports freigeschaltet/durchgeroutet sind. Das ist sicherheitsrelevant und wird warhscheinlich so weit wie möglich vermieden. Stattdessen solltest du im InTRAnet der Server eher die Möglichkeiten haben, eine entfernte DB zu benutzen.

        Es gibt auch noch andere Optionen, wie zum Beispiel ein PHP-basierte API auf anderen Server bereitzustellen und die Daten via HTTP-POST/GET auszutauschen: Stichworte SOAP und WebService.

        Grüsse
        Frank

  2. Also die Zahlen die du nennst hören sich nicht nach viel an. Leider gibst du keinen Zeitrahmen an in dem das statt findet. Aber selbst wenn diese 5200 Anfragen innerhalb von einer Stunde aufschlagen sollte das ( selbst mit nicht optimierten SQL-Statements) kein problem sein. Es sei denn du läst dir irrsinnig grosse Antwort listen zurückgeben und suchst in denen dann mit php mitteln.

    Deswegen denk ich mal das du im Hintergrund irgendwas sehr "zeit teures" machst. z.B. einen entfernten Server fragen ob der Brusche eingeschrieben ist.

    Das solltest du unbedingt "asynchron" machen:

    -via PHP/Perl Formular die Daten abfragen.
    -In lokale SQL stopfen.
    -Auf eine selbst refreshende Seite ( z.B. 1min ) schicken auf der "Bitte Warten Sie" steht die nur in die lokale DB schaut ob der eintrag als "bestätigt" makiert wurde.

    Dann machst du dir ein vom Webserver unabhängiges script ( z.B. via cronjob ) das in die lokale DB schaut und _seriell_ die Anfragen abarbeitet. Und dann den eintrag in der DB als "bestätigt" oder "abgelhnt" makiert.

    Das hätte zur Folge das der Apache seine limitierten verbindungen nicht unnötig lange offen halten muss weil das script arbeitet bzw. was ich stark vermute auf Antworten wartet.

    Selbst wenn da tatsächlich eine hohe CPU last entsteht würde das den Server nicht "stillegen" - Ein script bearbeitet im hintergund die anfragen und der Apache hat luft auf den User zu reagieren.

    Als "Bonus" könnte man sogar ne art "Zeitanzeige/Fortschritt" einbauen oder im schlimmsten fall sagen das er die Antwort dann irgendwann per Email bekommt.

    1. Hallo,

      Also die Zahlen die du nennst hören sich nicht nach viel an. Leider gibst du keinen Zeitrahmen an in dem das statt findet. Aber selbst wenn diese 5200 Anfragen innerhalb von einer Stunde aufschlagen sollte das ( selbst mit nicht optimierten SQL-Statements) kein problem sein. Es sei denn du läst dir irrsinnig grosse Antwort listen zurückgeben und suchst in denen dann mit php mitteln.

      In der Summe sind die Zahlen nicht viel, aber man muss - und das hab ich leider vergessen beim 1. Posting - sehen, dass der Großteil davon innerhalb weniger Minuten stattfindet. Das ganze wird durch das first-come-first-serve Prinzip natürlich verstärkt. Es sind ca. 2.500 Studenten eingeschrieben und die wollen in relativ kurzer Zeit eben eine begrenzte Anzahl von Plätzen ergattern.

      Zu den SQL-Statements noch ein Wort: ich hab sowieso da schon geschaut, nur das zurück zugeben, was in der weiteren Verarbeitung benötigt wird.

      Das solltest du unbedingt "asynchron" machen:

      -via PHP/Perl Formular die Daten abfragen.
      -In lokale SQL stopfen.
      -Auf eine selbst refreshende Seite ( z.B. 1min ) schicken auf der "Bitte Warten Sie" steht die nur in die lokale DB schaut ob der eintrag als "bestätigt" makiert wurde.

      Im Prinzip funktioniert es bereits jetzt so:

      1. Liste mit allen Kursen, zu denen sich Student X anmelden darf
      2. Dieser wählt einen Kurs aus
      3. eine Seite weiter muss er bestätigen (zwingend erfolgerlich)
      4. Daten werden in DB übernommen
      5. Liste mit allen Kursen wird geladen, der gewählte Kurs wird entsprechend markiert als "angemeldet"

      Dann machst du dir ein vom Webserver unabhängiges script ( z.B. via cronjob ) das in die lokale DB schaut und _seriell_ die Anfragen abarbeitet. Und dann den eintrag in der DB als "bestätigt" oder "abgelhnt" makiert.

      Das Problem hierbei ist, dass der Studenten sofort wissen soll, ober er den Platz hat oder nicht. Das heißt ich kann leider nicht erst alle Anfragen sammeln und später auswerten, wie das bei einem Präferenzsystem z. B. der Fall wäre (dort gibt man seine Präferenz für bestimmte Kurse an, und wenn die Frist vorbei ist werden die Kurse anhand der Präferenzen verteilt).

      Als "Bonus" könnte man sogar ne art "Zeitanzeige/Fortschritt" einbauen oder im schlimmsten fall sagen das er die Antwort dann irgendwann per Email bekommt.

      Das wäre klasse, aber wenn aufgrund der Auslastung nur noch eine weiße Seite angezeigt wird, bzw. überhaupt keine Reaktion vom Server mehr kommt hilft das auch nicht weiter.

      Grüße,
      Michael

      1. Neben den richtigen dingen die Frank angesprochen ( skalierung der Hardware hat ) clustern, Dinge wie ein Session server ...

        Nochmal Software Optimierungen:

        DB Optimierung:
        Passen die DB Indexe alle in das RAM? Brauchst du alle?

        Apache Connects/DB connects:
        Bauen die Apache Prozesse mehr DB verbindungen auf als die SQL zulässt?
        ( bin leider kein php entwickler. Unter Perl kann man das mit Apache::DBI sehr schön regeln das je nach anwendung pro Apache Child exact ein konnect gebraucht wird )

        Da du sehr viele kurze, verschiedene Besucher hast:
        Lager alle Seiten, Bilder etc auf einen anderen Server aus. Auch die Eingabe-Formulare. ( cache greift nicht )

        Script Optimierungen:
        Jetzt müsste man sich eigentlich dein Script durchlesen aber hier nochmal ein paar Ideen (ka was du da machst das kann auch eine verschlimbesserung sein):

        1. Liste mit allen Kursen, zu denen sich Student X anmelden darf

        Könntest du alle denkbaren kombinationen "vor rendern" die seiten statisch irgendwo ablegen und dem Stundent nach der Anmeldung nur noch entsprechend verlinken u.u. auch ein wenig dhtml das unztreffende kurse client seitig ausblendet?

        1. Liste mit allen Kursen wird geladen, der gewählte Kurs wird entsprechend markiert als "angemeldet"

        Liste wieder statisch laden und nur via DHTML ( client seitig ) die jenigen makieren die er ausgewählt hat.
        ( z.B. javascript parst url parameter und du verlinkst: http://statik.uniserver.edu/kursliste.html?angemldet=1,432,5322,54764,23
        )

        Wenn ein student hier bescheisst kann man ihn wenn "Ruhe" ist immer noch entfernen.

        Das primaäre problem ist das der Student sofort wissen soll ob er den platz hat oder nicht.

        Zum Thema "first-come-first-serve" Prinzip:

        Könnte ich den namen deiner Uni haben? Ich würde euren Studenten gerne einen Kostengünstigen Bot-Service anbieten sich Zeitnah nach freischaltung der Seiten anzumelden. 800 - 1000 Anmeldungen pro minute sollte ich locker schaffen. Oder sollte das etwas schon jetzt dein problem sein ? ( *fg* )

        Ich würde doch mal /dev/random empfehlen damit nicht nur die Kumpels von den Informatikern die ersten plätze bekommen. Erinnert mich irgendwie an so ein nettes Script das ein Freund von mir mal geschrieben hatten das einem in dem Moment ne SMS geschickt hat wenn ein beliebtes und "seltenes" Buch in UniBibliothek zurückgegeben wurde - da man die nicht Reservieren durfte ^^. War auch so'ne schlaue First-come-first-serve Regelung.

        1. Morgen,

          [...]

          1. Liste mit allen Kursen, zu denen sich Student X anmelden darf

          Könntest du alle denkbaren kombinationen "vor rendern" die seiten statisch irgendwo ablegen und dem Stundent nach der Anmeldung nur noch entsprechend verlinken u.u. auch ein wenig dhtml das unztreffende kurse client seitig ausblendet?

          Nein, leider nicht. Diese Liste hängt erstens vom jeweiligen Studenten und zweiten von den anderen Studenten mit ab. D. h. meldet sich Student Y für Kurs A an, dann hat Student X für Kurs A wieder eine Möglichkeit weniger, sich dort einzutragen. Das ist jetzt ganz einfach beschrieben. Aber die Vorgaben des Instituts sind vielschichtig und es gibt eine Menge von Regeln für die Kursanmeldungen ...

          1. Liste mit allen Kursen wird geladen, der gewählte Kurs wird entsprechend markiert als "angemeldet"
            Liste wieder statisch laden und nur via DHTML ( client seitig ) die jenigen makieren die er ausgewählt hat.
            ( z.B. javascript parst url parameter und du verlinkst: http://statik.uniserver.edu/kursliste.html?angemldet=1,432,5322,54764,23
            )

          Das bringt u. a. aus obigem Grund auch nicht viel bzw. würde letztlich zum gleichen Ergebnis führen, da ich alle relevanten Daten in die URL packen muss. Ob ich nun die URl mit den Daten vollpacke oder gleich die gesamte Seite damit erstelle dürfte m. E. kein wesentlicher Unterschied sein.

          Zum Thema "first-come-first-serve" Prinzip:

          Tja, mir gefällt dieses Prinzip auch nicht wirklich, aber das war die Vorgabe.

          Mittlerweile hab ich wieder einmal ein Präferenzsystem vorgeschlagen (in der Vergangenheit war das Institut da leider immer sehr abgetand davon), und vielleicht entscheiden sie sich doch jetzt dafür...

          Grüße,
          Michael

          1. Tach,

            Nein, leider nicht. Diese Liste hängt erstens vom jeweiligen Studenten und zweiten von den anderen Studenten mit ab. D. h. meldet sich Student Y für Kurs A an, dann hat Student X für Kurs A wieder eine Möglichkeit weniger, sich dort einzutragen. Das ist jetzt ganz einfach beschrieben. Aber die Vorgaben des Instituts sind vielschichtig und es gibt eine Menge von Regeln für die Kursanmeldungen ...

            Üble anforderung. Je nachdem wie verbindlich deine Bestätigung ist fallen mir da so "Schlagworte" wie Transaktionssicherheit und Atomar ein. MySQL fängt an "procedures" zu unterstützen. Also DB Seitige Funktionen. Die könnten da helfen - Aber hab ich keine Erfahrung auf MySQL basis mit.

            Das bringt u. a. aus obigem Grund auch nicht viel bzw. würde letztlich zum gleichen Ergebnis führen, da ich alle relevanten Daten in die URL packen muss. Ob ich nun die URl mit den Daten vollpacke oder gleich die gesamte Seite damit erstelle dürfte m. E. kein wesentlicher Unterschied sein.

            Mit DHTML und Javascript kann man eine Menge Clientseitig machen. Dein Student Y hängt von Student X ab ist hier ein problem. Trotzdem könnte es sich lohnen zu überlegen welche Informationen ( auch wenn sie komplex sind ) Sich innerhalb der "Heissen-Phase" nicht ändern. z.B Deutsch, Biologie auf Lehramt im 5. Semester darf kurse X belegen --> /05/lehramt/deutsch/biologie/liste.html - Natürlich würde diese Liste dann auch alle "vollen" Kurse enthalten. Dann würde der Anemlde Versuch "scheitern", was unkomfortabel ist. Aber alleine das anlegen von 20000 Kreuzprodukten aus deiner "KursDB" ist schon suboptimal.

            Denn die Optimale Lösung:

            Zum Thema "first-come-first-serve" Prinzip:

            Tja, mir gefällt dieses Prinzip auch nicht wirklich, aber das war die Vorgabe.

            Mittlerweile hab ich wieder einmal ein Präferenzsystem vorgeschlagen (in der Vergangenheit war das Institut da leider immer sehr abgetand davon), und vielleicht entscheiden sie sich doch jetzt dafür...

            Als gutes Argument - Nein es ist nicht gut - sagen wir "gut funktionierendes Argument" - folge dochmal "Frank's"-Weg und lass dir mal nen Angebot machen was z.B. die Miete für ein Application-Cluster für eine Woche kosten würde der den Peak von 1200 Anmeldungen in 5min schaffen kann.

            Grüße,
            Michael