Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Re: Ersatz für SQL bei Prozessverwaltung gesucht (Viele zeitgleiche Schreibzugriffe)

9 views
Skip to first unread message

Peter J. Holzer

unread,
Jun 30, 2017, 10:02:35 AM6/30/17
to
[Ich lenke mal auf de.comp.datenbanken.misc um - mit PHP hat das gar
nichts mehr zu tun]

On 2017-06-30 04:56, Michael Vogel <i...@spamfence.net> wrote:
> Am 28.06.2017 um 11:30 schrieb Peter J. Holzer:
>> On 2017-06-27 16:17, Michael Vogel <i...@spamfence.de> wrote:
>>> Mein Server liegt auf einem Server, der eindeutig im I/O-Bereich
>>> überlastet ist. Die Ursache liegt dabei nicht an einer Fehlkonfiguration
>>> des Servers, sondern an den Prozessen. Es laufen dort vier soziale
>>> Netzwerke parallel.
>>
>> Einen Server, die "eindeutig im I/O-Bereich überlastet" ist, würde ich
>> als Systemadministrator schon als "Fehlkonfiguration" bezeichnen. Kann
>> natürlich sein, dass man wenig dagegen tun kann, weil das Budget knapp
>> ist. Aber Arbeitszeit ist auch nicht gratis - auch nicht bei einem
>> Freizeitprojekt.
>
> Ich bin nur "Freizeitadmin", aber nach allem, was ich zum Server sagen
> kann, ist er nicht fehlkonfiguriert, sondern einfach nur überlastet.

Wenn Du (z.B.) 4 Services, die jeweils 100 Requests pro Sekunde
bearbeiten sollen, auf einem Host installierst, der nur 300 Requests
pro Sekunde schafft, dann ist das für mich eine Fehlkonfiguration.
Konfigurationsänderungen, die das Problem beheben könnten, wären z.B.:

* Verlagern mindestens eines der Services auf einen anderen Host

* Ausbau des vorhandenen Hosts (zusätzliche und/oder schnellere Disks,
mehr RAM, ...), damit er mehr Requests pro Sekunde abarbeiten kann.

* Reorganisation der Disks: Anderes RAID-Level, bestimmte "heiße"
Tabellen besser verteilen, ...

Die Konfiguration eines Systems besteht nicht nur aus Config-Files.

> Ich könnte mir natürlich einen (noch) größeren Server besorgen.
> Allerdings läuft die Software an der ich arbeite ja nicht nur auf
> meinem Rechner, sondern noch auf hunderten anderer Systeme. Dadurch,
> dass mein System relativ langsam ist, kann ich viel besser erkennen,
> welche Programmierungen welche Auswirkungen in Bezug auf Performance
> haben. Das sehe ich als Vorteil.

Ob Deine User das auch so sehen? Aber egal. Das ist Deine Entscheidung.


>>>>> Wie gut ist "Berkeley DB" dafür geeignet? Siehe
>>>>> http://php.net/manual/de/intro.dba.php
>>>>
>>>> BDB war einmal eine der MySQL-Engines. Wurde zugunsten von InnoDB
>>>> entfernt (hatte WIMRE auch Lizenzgründe). Seitdem wurde es offenbar
>>>> recht kräftig weiterentwickelt, ich kann also zum aktuellen Status
>>>> nichts sagen. Bauchgefühl sagt: Wird wohl nicht viel anders als InnoDB
>>>> sein.
>>>
>>> Sperren gehen auch?
>>
>> Eigentlich willst Du da keine Sperren, Die sind eher eine unerwünschte
>> Nebenwirkung.
>
> Wenn ein Worker gerade die Queue abarbeitet, während der Scheduler die
> Queue auffüllt, muss das ja schon irgendwie synchronisiert werden, damit
> kein Chaos entsteht.

Richtig. Du willst, dass das "irgendwie synchronisiert" wird. Locks sind
eine Methode zur Synchronisation. Du schließt daraus, dass Du Locks
willst. Das ist aber ein Fehlschluss. Locks sind nicht das Ziel, sondern
ein Weg zum Ziel. Wenn Du das Ziel auf anderem Weg erreichen kannst, ist
das genauso gut und vielleicht sogar besser.

Konkret hast Du das Problem, dass Deine Locks entweder zu lange dauern
oder zu viel locken. Prozesse warten daher darauf, dass ein anderer
Prozess endlich einen Lock freigibt, statt sinnvolle Arbeit zu
verrichten. So habe ich Deine Beschreibung zumindest verstanden (wobei
das ein bisschen im Widerspruch zur Beobachtung steht, dass das System
jetzt bereits überlastet ist - wenn die Prozesse weniger warten würden,
wäre das System ja noch stärker belastet).

Du willst also Locks möglichst minimieren. Idealerweise willst Du sie
ganz loswerden, das wirst Du allerdings mit einem RDBMS eher nicht
schaffen, weil jedes RDBMS intern Locks verwendet - allerdings
(hoffentlich) sehr viel feinere, als Du das als Applikations-
programmierer kannst.

Gehen wir mal ein paar Möglichkeiten durch:

1. Die Worst-Practice Methode:

Du hast eine Queue-Tabelle. Wenn ein Worker einen Job abarbeiten
will, sperrt er die ganze Tabelle, schaut nach, ob er einen Job
findet, wenn ja, arbeitet er ihn ab und löscht ihn aus der Tabelle.
Ganz zum Schluss gibt er die Tabelle wieder frei.

Das ist offensichtlich kompletter Unsinn: Es kann immer nur ein
Worker gleichzeitig arbeiten und man kann nicht einmal neue Jobs
einwerfen, solange ein Job arbeitet.

Das muss besser gehen.

2. select for update

Der obige Ansatz sperrt die Tabelle länger als notwendig (während
der Job abgearbeitet wird, muss sie nicht gesperrt sein) und er
sperrt mehr als notwendig (die ganze Tabelle, es müsste aber nur
eine Zeile gesperrt werden. Das lässt sich leicht verbessern:

Job holen:

select id, ...
from job_queue
where status = 'new'
limit 1
for update;
update job_queue set status = 'in progress' where id=...;
commit;

Job löschen:

delete from job_queue where id=...;
commit;

Damit ist der Lock sehr viel kürzer (er existiert nur mehr für die
Zeit zwischen select und update, und sehr viel feiner granuliert
(nur mehr eine Zeile).

Aber es gibt da immer noch ein Problem: Wenn zwei Worker
gleichzeitig einen neuen Job suchen, werden sie wahrscheinlich den
gleichen finden, und der zweite wird auf den ersten warten, nur um
festzustellen, dass die Zeile, die er gerade locken wollte, die
where-Klausel nicht mehr erfüllt.

3. select for update skip locked

PostgreSQL löst das Problem mit der Option skip locked. Der zweite
Worker wird in dem Fall einfach die bereits vom ersten gelockte
Zeile auslassen und die nächste Zeile, die die where-Klausel
erfüllt, sperren. Aus Sicht des Anwendungsprogrammiers ist das
Lock-Frei: Er wartet nie, sondern bekommt immer "sofort" eine
Antwort: Entweder einen Job oder eben keinen, wenn nichts zu tun
ist.

4. Andere Ansätze:

Das Hauptproblem bei Ansatz 2 ist, dass es zu deterministisch ist:
Alle Worker versuchen sich auf den gleichen Job zu stürzen, auch
wenn mehrere in der Queue sind. Das könnte man z.B. durch Zuordnung
zu verschiedenen Worker-Klassen (eventuell sogar in getrennten
Queue-Tabellen!), zufällige Sortierung, o.ä. umgehen.

Oder man könnte einen optimistischen Update machen:
update job_queue
set status='in progress', worker_id=...
where status = 'new'
limit 1;
commit;
select * from job_queue where worker_id=...;
Keine Garantie, dass das besser ist als select for update, aber es
ist einen Versuch wert.


>> PostgreSQL kennt "select for update ... skip locked", das für Job-Queues
>> und ähnliche Anwendungen ideal sein sollte. Schon vorher wurden damit
>> recht ansehnliche Durchsatzzahlen erreicht:
>> https://gist.github.com/chanks/7585810
>
> Es ist auf alle Fälle bei uns auf dem Plan, dass wir auch Postgres
> unterstützen, aber soweit sind wir noch nicht - und vor allem können wir
> es unseren Usern nicht vorschreiben.

Nein, aber "auch PostgreSQL unterstützen" heißt ja nicht, den Usern
vorzuschreiben, dass sie das nützen müssen. Und selbst wenn in der Doku
steht "für Sites mit vielen Benutzern empfehlen wir PostgreSQL", ist das
immer noch nur ein Rat und kein Zwang. Wenn Du ein komplett anderes
Datenbanksystem für die Queues verwendest (BDB oder Redis oder was auch
immer), dann ist das eher ein Zwang, denn das müssen die Leute dann
wahrscheinlich installieren, außer Du willst mehrere Varianten
unterstützen.


>>> Mir het jemand jetzt auch SHM empfohlen. ggf. kann ich auch was mit
>>> Semaphoren machen, das bin ich gerade am Planen.
>>
>> Hattest Du nicht geschrieben, dass Lösungen, die nur im RAM arbeiten,
>> für Dich nicht in Frage kommen?
>
> Semaphoren und SHM dienen ja nur der Kommunikation und Koordination der
> Prozesse.

Probieren kann man viel, aber ich sehe keinen Grund zur Annahme, dass
es besser ist, wenn Du in der Applikation noch einen zweiten
Synchronisationslayer um den bereits in der Datenbank existierenden
herumbaust, wenn Du dann die Daten doch in der Datenbank speicherst.
Sieht für mich nur nach zusätzlichem Overhead aus - inklusive der Gefahr
heftige Bugs einzubauen - das ist nämlich nicht trivial.

hp


--
_ | Peter J. Holzer | Fluch der elektronischen Textverarbeitung:
|_|_) | | Man feilt solange an seinen Text um, bis
| | | h...@hjp.at | die Satzbestandteile des Satzes nicht mehr
__/ | http://www.hjp.at/ | zusammenpaßt. -- Ralph Babel

Michael Vogel

unread,
Jun 30, 2017, 11:03:01 AM6/30/17
to
Peter J. Holzer wrote:

> [Ich lenke mal auf de.comp.datenbanken.misc um - mit PHP hat das gar
> nichts mehr zu tun]

> On 2017-06-30 04:56, Michael Vogel <i...@spamfence.net> wrote:
>> Ich bin nur "Freizeitadmin", aber nach allem, was ich zum Server sagen
>> kann, ist er nicht fehlkonfiguriert, sondern einfach nur überlastet.
>
> Wenn Du (z.B.) 4 Services, die jeweils 100 Requests pro Sekunde
> bearbeiten sollen, auf einem Host installierst, der nur 300 Requests
> pro Sekunde schafft, dann ist das für mich eine Fehlkonfiguration.

Es ist ein Mietserver, auf dem ich so oder so nicht alles machen kann,
wie z.B. Hardware-Änderungen. Leider erlaubt mein Hoster das
nicht.

>> Ich könnte mir natürlich einen (noch) größeren Server besorgen.
>> Allerdings läuft die Software an der ich arbeite ja nicht nur auf
>> meinem Rechner, sondern noch auf hunderten anderer Systeme. Dadurch,
>> dass mein System relativ langsam ist, kann ich viel besser erkennen,
>> welche Programmierungen welche Auswirkungen in Bezug auf Performance
>> haben. Das sehe ich als Vorteil.
>
> Ob Deine User das auch so sehen? Aber egal. Das ist Deine Entscheidung.

Meine User wissen, dass der Server ein Test- und Enrwicklungsserver ist.
Sie nehmen auch kurzfristige Instabilitäten und seltsames Verhalten in
Kauf. Wenn sie einen stabilen Server haben möchten, könnten sie sich
einen anderen aussuchen oder selber einen installieren.

Mal davon abgesehen fühlt sich das System in der Tat sehr flüssig an.

> Du willst also Locks möglichst minimieren. Idealerweise willst Du sie
> ganz loswerden, das wirst Du allerdings mit einem RDBMS eher nicht
> schaffen, weil jedes RDBMS intern Locks verwendet - allerdings
> (hoffentlich) sehr viel feinere, als Du das als Applikations-
> programmierer kannst.

Ich sag mal "jein", da die verschiedenen Techniken verschiedene
Probleme haben.

> Gehen wir mal ein paar Möglichkeiten durch:
>
> 1. Die Worst-Practice Methode:
>
> Du hast eine Queue-Tabelle. Wenn ein Worker einen Job abarbeiten
> will, sperrt er die ganze Tabelle, schaut nach, ob er einen Job
> findet, wenn ja, arbeitet er ihn ab und löscht ihn aus der Tabelle.
> Ganz zum Schluss gibt er die Tabelle wieder frei.

Das hatte ich mit den Table-Lock-Befehlen ausprobiert und in der Tat war
das Ergebnis suboptimal ;-) (Aber ich probiere das immer lieber selber
aus)

> 2. select for update

Hatte ich auch schon ausprobiert - und genau das Problem:

> Aber es gibt da immer noch ein Problem: Wenn zwei Worker
> gleichzeitig einen neuen Job suchen, werden sie wahrscheinlich den
> gleichen finden, und der zweite wird auf den ersten warten, nur um
> festzustellen, dass die Zeile, die er gerade locken wollte, die
> where-Klausel nicht mehr erfüllt.

> 3. select for update skip locked
>
> PostgreSQL löst das Problem mit der Option skip locked.

Kann ich mangels Postgres (noch) nicht machen.

> 4. Andere Ansätze:
>
> Das Hauptproblem bei Ansatz 2 ist, dass es zu deterministisch ist:
> Alle Worker versuchen sich auf den gleichen Job zu stürzen, auch
> wenn mehrere in der Queue sind. Das könnte man z.B. durch Zuordnung
> zu verschiedenen Worker-Klassen (eventuell sogar in getrennten
> Queue-Tabellen!), zufällige Sortierung, o.ä. umgehen.

Meine jetzige Lösung sieht anders aus - und ist sehr effektiv. Ich setze
zu Anfang eine Semaphore. Dann hole ich mir die abzuarbeitenden Aufträge
per SELECT-Query und führe danach eine Update-Query über das Ergebnis
aus. Danach entferne ich die Semaphore.

Da die Update-Query nur auf die ID-Felder geht, gibt es keine
Sperrprobleme und die Semaphoren werden nur kurz gehalten.

Gleichzeitig hole ich mir zwischendrin auch mal freie Jobs, falls ich
eine Semaphore ohne Wartezeit sperren kann:
https://github.com/annando/friendica/blob/some-more-poller/include/poller.php#L105-L111

>> Es ist auf alle Fälle bei uns auf dem Plan, dass wir auch Postgres
>> unterstützen, aber soweit sind wir noch nicht - und vor allem können wir
>> es unseren Usern nicht vorschreiben.
>
> Nein, aber "auch PostgreSQL unterstützen" heißt ja nicht, den Usern
> vorzuschreiben, dass sie das nützen müssen. Und selbst wenn in der Doku
> steht "für Sites mit vielen Benutzern empfehlen wir PostgreSQL", ist das
> immer noch nur ein Rat und kein Zwang.

Ja, klar. Aber wenn auch MySQL laufen muss, können wir bestimmte Dinge
nicht machen, bzw. brauchen alternative Wege dafür.

> Wenn Du ein komplett anderes
> Datenbanksystem für die Queues verwendest (BDB oder Redis oder was auch
> immer), dann ist das eher ein Zwang, denn das müssen die Leute dann
> wahrscheinlich installieren, außer Du willst mehrere Varianten
> unterstützen.

Aufgrund unserer Philosophie könnten wir das sowieso nicht.

>>> Hattest Du nicht geschrieben, dass Lösungen, die nur im RAM arbeiten,
>>> für Dich nicht in Frage kommen?
>>
>> Semaphoren und SHM dienen ja nur der Kommunikation und Koordination der
>> Prozesse.
>
> Probieren kann man viel, aber ich sehe keinen Grund zur Annahme, dass
> es besser ist, wenn Du in der Applikation noch einen zweiten
> Synchronisationslayer um den bereits in der Datenbank existierenden
> herumbaust, wenn Du dann die Daten doch in der Datenbank speicherst.
> Sieht für mich nur nach zusätzlichem Overhead aus - inklusive der Gefahr
> heftige Bugs einzubauen - das ist nämlich nicht trivial.

Das mit den Semaphoren hat wunderbar geklappt. Die Performance ist um
den Faktor 2-3 gestiegen und der Overhead massiv gesunken.

Michael
0 new messages