Reloadsperre

xdragonx

Well-known member
ID: 310927
L
4 März 2008
348
11
Hi,

Sitze zurzeit an einer Erweiterung, welche mit Reloadsperren arbeiten soll.

In dem Fall beträgt sie 24h. Jetzt die Frage, wie ich das lösen kann, ohne dass viele Resourcen verschwendet werden.

Nehmen wir mal an es sind 500 Seiten zum Klicken da und 10.000 User sind angemeldet die alle aktiv klicken. Dann muss für jeden Klick ein Timestamp angelegt werden und immer überprüft werden, wann dieser abläuft.

Sind also wenn alle User an einem Tag alle 500 Seiten klicken, 5 Mio. Datensätze. Das sind eindeutig zuviele. Mir fällt aber gerade kein anderer Lösungsweg ein.

Loseseiten kriegen dies ja auch hin, wenn nicht mit ganz sovielen Usern aber mehreren Seiten zum Klicken. Cookies ist auch keine gute Idee, da diese manipulierbar sind. Die Suchfunktion hat leider nichts ergeben.

Gruss xdragonx
 
Sind also wenn alle User an einem Tag alle 500 Seiten klicken, 5 Mio. Datensätze. Das sind eindeutig zuviele.

Was stört dich daran? Datenbanken sind für solche Probleme ausgelegt.
Wenn du noch die Indexe richtig setzt wirst du auch keine Performance-Probleme bekommen.

Eine Andere nicht manipulierbare Lösung wäre mir nicht bekannt.
 
Was stört dich daran? Datenbanken sind für solche Probleme ausgelegt.
Wenn du noch die Indexe richtig setzt wirst du auch keine Performance-Probleme bekommen.

Eine Andere nicht manipulierbare Lösung wäre mir nicht bekannt.

Hab gerade eine andere Möglichkeit gefunden, aber werde beide Möglichkeiten auf ihre Performance testen.

Hätte nicht gedacht, dass MySQL soviele Datensätze schnell bearbeiten kann.

Aufjedenfall erstmal Danke für die Hilfe.
 

Jeder User sprich jeder Datensatz hat ein Feld, wo die ID's der geklickten Links inkl. Timestamp eingefügt werden.

Wenn der User nun die Linkliste aufruft oder einen Link anklickt, wird überprüft, ob der erste Eintrag im Feld abgelaufen ist, und gelöscht. Dies geschieht solange, bis ein Eintrag gefunden wurde, der noch nicht abgelaufen ist.

Somit hat man nur einen Datensatz pro User. Wie schnell dies abgearbeitet wird, werde ich testen.
 
Solche Schleifen (und das wirds im Endeffekt werden) in PHP dauern erfahrungsgemäß immer länger, als wenn eine gut gestrickte SQL-Query 5Mio Datensätze durchforstet.

Und der Performance wird es auch keinen abbrechen, da die 5 Mio Datensätze auch nicht binnen 3sek generiert werden.

Außerdem willst Du ja nur die Links anzeigen die nicht im reload sind. Wenn ich Deinen Plan richtig verstehe, willst Du alle Links abrufen, und für jeden Link beim aktuellen User prüfen ob reload oder nicht.

Mach so wie Xot vorgeschlagen hat. Also extra Tabelle mit userid, linkid und aktueller Timestamp + 86400sek.

Sinngemäß:
=> DELETE FROM sperrliste WHERE timestamp < time()-86400 (kann man auch 5minütlich per CRON laufen lassen, dann ist's aus dem Script raus)

=> SELECT linkid WHERE userid = $userid -> in ein array $reload
=> SELECT * FROM links WHERE linkid NOT IN (implode(',', $reload))

Die Queries sind jetzt nur "dahingeklatscht" ohne Rücksicht auf Syntax, und lassen sich auch eleganter gestalten. Aber MySQL kriegt das hin...
 
Jeder User sprich jeder Datensatz hat ein Feld, wo die ID's der geklickten Links inkl. Timestamp eingefügt werden.

Also hätte dieses Feld eine maximale Länge von
500 Seiten * (4stellige Seiten-Id (bissel Luft sollte schon sein) + 10-stelliger Timestamp + 2 Trennzeichen) = 8000 Zeichen.

Wenn der User nun die Linkliste aufruft oder einen Link anklickt, wird überprüft, ob der erste Eintrag im Feld abgelaufen ist, und gelöscht. Dies geschieht solange, bis ein Eintrag gefunden wurde, der noch nicht abgelaufen ist.
Und wer soll das machen? MySQL? PHP? Für MySQL siehts da wohl schlecht aus und bei PHP müsstest du letztlich auch den kompletten Inhalt analysieren, da ja theoretisch auch ein Aufruf einer ID stattfinden könnte, die irgendwo in der Mitte oder am Ende des Strings steht. Also alles schön mit explode trennen und hinterher wieder zusammenflicken.

Somit hat man nur einen Datensatz pro User.
Einen sehr langen, unhandlichen und nicht normalisierten.

Wie schnell dies abgearbeitet wird, werde ich testen.
Ich tippe auf die Tabelle mit den vielen Datensätzen.
 
Wenn ich das richtig verstehe hast du dann folgende Felder:
UserID, SiteIDs, Timestamp

Damit würdest du dann aber gegen die 1. Normalform verstossen.

Sind nur 2 Felder.

UserID und SiteID - Timestamp

SiteID und Timestamp sind durch einen Bindestrich getrennt und befinden sich in einem Feld. Per Komma, kommen weitere SIteID's+Timestamps hintendran. Diese werden per Explode aufgeteilt und dann überprüft.

Wielange dauert es denn bei einem halbwegs guten vServer mit 2GB Ram garantiert, um alle abgelaufenen Klicks aus einer Tabelle mit 5 Mio. Datensätzen zu löschen?

Und wer soll das machen? MySQL? PHP? Für MySQL siehts da wohl schlecht aus und bei PHP müsstest du letztlich auch den kompletten Inhalt analysieren, da ja theoretisch auch ein Aufruf einer ID stattfinden könnte, die irgendwo in der Mitte oder am Ende des Strings steht. Also alles schön mit explode trennen und hinterher wieder zusammenflicken.

Die ID's der geklickten Seiten in dem Feld sind geordnet. Heißt, dass die erste ID aufjedenfall als erstes gelöscht werden muss. Also das FIFO - Prinzip
 
Zuletzt bearbeitet:
Das mag ja sein. Aber du musst doch zwischendurch auch verweigern können, wenn eine ID noch in der Reloadsperre ist, obwohl du gar nicht damit rechnest, dass sie gerade aufgerufen wird.

Ich weiß ja nicht wie dein System aufgebaut ist. Vermutlich ist es aber irgendwie möglich GET oder POST Parameter zu manipulieren und damit eine ID aufzurufen, die eigentlich gar nicht in der Liste der aktuell verfügbaren IDs angezeigt war.

Und spätestens wenn du die Liste der verfügbaren IDs darstellen möchtest, musst ja den kompletten Datensatz auslesen, um die ausgeschlossenen IDs zu kennen.

Und das wäre datenbankintern ohne Stringmanipulation leicht möglich. Wenn du eine Tabelle links (id, url, titel, ..) hast und eine Tabelle reloadsperre (linkid, userid, datetime), könntest du einfach in einer einzigen Abfrage einen LEFT JOIN auf links machen, per userid verknüpfen und nach zeitpunkt aussortieren. Dürfte grob geschätzt deutlich schneller sein als irgendwelche Stringmanipulationen.
 
Ich weiß ja nicht wie dein System aufgebaut ist. Vermutlich ist es aber irgendwie möglich GET oder POST Parameter zu manipulieren und damit eine ID aufzurufen, die eigentlich gar nicht in der Liste der aktuell verfügbaren IDs angezeigt war.

Manipulierbar ist nichts, da ja überprüft wird, ob die Seite noch in der Reloadsperre ist oder nicht. Und falls nicht, ist sie gar nicht mehr in der Liste.

Werden gleich ein paar Tests gestartet um zu sehen, was im Endeffekt schneller arbeitet. Melde mich später nochmal.

Recht hast du schon mit den Stringmanipulationen. Die Tests werden es zeigen.

E: Okay war eindeutig. Ich befolge euern Rat ;)
 
Zuletzt bearbeitet:
Wenn du die Wahl hast, dann nutze InnoDB.
Damit kannst du weit besser einstellen, wieviel Speicher die Tabellen im RAM nutzen dürfen. In dieser Folge kannst du die DB deutlich besser optimieren.

Dann noch ein-zwei kleine Tipps:
- nimm drei Spalten (wurde schon mehrfach genannt), Integer/Datetime sind immer schneller als Varchar/Text.
- prüfe lediglich ob die Kombination UserID + SiteID bereits in der Tabelle vorhanden ist (mehr nicht!)
- Nutze den Timestamp um per Cron-Job alle veraltete Datensätze zu löschen (nur dieser Prozess ist kritisch!)
- Lege einen Index auf UserID+SiteID
- Lass die Tabelle hin und wieder optimieren.

Füge den Datensatz ein. Gelingt das, führst du deine eigene Programmlogik weiter. Gelingt das nicht (schon vorhanden), brichst du ab.
Wenn du es in dieser Form umsetzt, erreichst du die maximale Performance.

Das Problem hatte ich vor kurzen ebenfalls in einer etwas komplexeren Form gelöst. ;)

Optional kannst du in deinem Fall zusätzlich mit Cookies arbeiten. Wird ein Cookie mitgeliefert, kannst du die weiteren Prüfungen direkt umgehen. Aber Achtung, Cookies haben Größenbeschränkungen.
 
Zuletzt bearbeitet:
Optional kannst du in deinem Fall zusätzlich mit Cookies arbeiten. Wird ein Cookie mitgeliefert, kannst du die weiteren Prüfungen direkt umgehen. Aber Achtung, Cookies haben Größenbeschränkungen.
... und Cookies sind Userdaten, d.h. auf die is eh kein Verlass und ergo kannst du sie für diesen Zweck vergessen ;)
 
... und Cookies sind Userdaten, d.h. auf die is eh kein Verlass und ergo kannst du sie für diesen Zweck vergessen ;)

Das ist zwar richtig, in diesem Fall aber zuweit gedacht.

Damit wird nur ein User ausgeschlossen. Hat er das Cookie nicht, greift trotzdem die Datenbankprüfung.

Nicht jeder User wird diese Cookies löschen, insofern könnte man es durchaus als zusätzlich Vorfilterung nutzen.
 
Und wenn Cookies nicht gelöscht sondern noch da sind, dann kann man sich auf ihre Unmanipulierbarkeit verlassen? ;)
 
Was soll denn eigentlich im Cookie gespeichert werden?

Ich denke im Cookie werden alle Links, die sich im Reload befinden gespeichert und werden vom Query automatisch ausgeschlossen zB.
WHERE id NOT IN $_COOKIE

Sollte nun jemand das Cookie entfernen wird dennoch in der Datenbank der Reload geprüft.

Aber ob das ganze wirklich viel Performancegewinn bringt ist fraglich, bei der geringen Anzahl Einträge.
 
Na siehste. Und wie schwer hätte es dann jemand, zwar das Cookie zu behalten, darin aber alle bis auf einen Sperreintrag zu löschen, um damit immer wieder die Reloadsperre zu umgehen?

Nun man könnte statt den Urls deren IDs in ein Array packen, es serializieren und verschlüsseln (nicht hashen) und das Ergebnis ins Cookie schreiben. Wenn bei der Umkehrung Müll rauskommt wurde manipuliert. Aber ob das wirklich irgendeinen Geschwindigkeitsvorteil bringen könnte :think:


Edit:
Da fällt mir noch was auf
Code:
WHERE id NOT IN $_COOKIE
:ugly::nö::doh:
 
Na siehste. Und wie schwer hätte es dann jemand, zwar das Cookie zu behalten, darin aber alle bis auf einen Sperreintrag zu löschen, um damit immer wieder die Reloadsperre zu umgehen?

Die Reloadsperre wird ja weiterhin Serverseitig überprüft, nur alle Einträge die im Cookie sind werden nicht mehr geladen. Was bringt es nun dem User im Cookie neue Einträge hinzuzufügen? :ugly: Und sollten Einträge aus dem Cookie gelöscht werden, wird das ganze immernoch in der DB zu finden sein.

Und nun um das Code-Beispiel noch zu vervollständigen...
PHP:
$reload = array(1, 2, 3);
setcookie("reload", serialize($reload));

Und dann halt wieder...

$reload = unserialize($_COOKIE["reload"]);
WHERE id NOT IN $reload;