Paralleler Zugriff auf Datenbanken



  • Was ist eigentlich die übliche Vorgehensweise, wenn man mit mehreren Threads auf eine Datenbank zugreift? Die Threads können dabei auch in gleichen Tabellen lesen und schreiben. Ist es da am sinnvollsten nur eine Connection zur DB zu haben und die anderen Threads zu Blocken oder gibts da was besseres?



  • Überlass' das dem Datenbanksystem. Ee ist für die Benutzersynchronization zuständig. Du kannst Anweisungen in Transaktionen zusammenfassen
    http://de.wikipedia.org/w/index.php?title=Transaktion_(Informatik)&stable=1
    Dort kannst Du auch das Isolationslevel einstellen:
    http://de.wikipedia.org/wiki/Isolation_(Datenbank)
    Versuche es nicht auf Anwendungsebene zu lösen, da Du ja nicht den Zugriff anderer Anwendungen auf die DB kontroliieren kannst.



  • Wie läuft das dann ab, wenn zwei Threads in zwei Tabellen schreiben?
    Z.B.
    Thread A will in T1 und T2 schreiben
    Thread B will in T2 und T1 schreiben
    Kann es dann nicht zu einem Deadlock kommen, wenn A nicht in T2 schreiben kann, weil B da was rein geschrieben, aber noch nicht commited hat, weil B noch was in T1 schreiben will, aber A da schon was rein geschrieben und noch nicht commited hat?



  • qwedfghj schrieb:

    Wie läuft das dann ab, wenn zwei Threads in zwei Tabellen schreiben?
    Z.B.
    Thread A will in T1 und T2 schreiben
    Thread B will in T2 und T1 schreiben
    Kann es dann nicht zu einem Deadlock kommen, wenn A nicht in T2 schreiben kann, weil B da was rein geschrieben, aber noch nicht commited hat, weil B noch was in T1 schreiben will, aber A da schon was rein geschrieben und noch nicht commited hat?

    LOCK TABLE
    

    😉



  • Ich persönlich mache es so, dass ich pro Thread eine eigene Connection verwende, egal wie viele Statements ich innerhalb eines Threads gleichzeitig laufen habe.

    Nur wenn ich mehrere Statements zu einer Transaktion zusammenfasse, mache ich dafür eine weitere Connection auf, weil eine Transaktion immer pro Connection gilt. Meistens sind das aber Einzelstatements, so dass ich Autocommit verwende und somit mit einer Connection auskomme.

    Was den Isolation-Level angeht, das ist ein Attribut des Statements, nicht der Connection. Ist daher für die Anzahl der Connections völlig egal. Kann ausserdem jederzeit für ein einzelnes Statement geändert werden, sowohl per API als auch direkt per SQL (SELECT BLAH FROM SUELZ WITH UR würde z.B. dieses eine Statement mit Uncommitted Read ausführen, also auch diejenigen Datensätze liefern, die noch nicht committed wurden).

    Das gilt zumindest für ODBC sowie für das Native API meiner DB. Keine Ahnung ob das allgemeingültig ist.



  • sothis_ schrieb:

    qwedfghj schrieb:

    Wie läuft das dann ab, wenn zwei Threads in zwei Tabellen schreiben?
    Z.B.
    Thread A will in T1 und T2 schreiben
    Thread B will in T2 und T1 schreiben
    Kann es dann nicht zu einem Deadlock kommen, wenn A nicht in T2 schreiben kann, weil B da was rein geschrieben, aber noch nicht commited hat, weil B noch was in T1 schreiben will, aber A da schon was rein geschrieben und noch nicht commited hat?

    LOCK TABLE
    

    😉

    Hmh, Du kannst auf Tabellenebene sperren, könnte aber bei massigen Zugriff die Performance der DB stark beeinträchtigen. Des weiteren muss sichergestellt sein, dass der Lock möglichst kurzzeitig erfolgt, "Kollege ist beim Essen und hat noch die Applikation offen" ist da der Klassiker. Außerdem schützt LOCK TABLE nicht vor dem oben beschriebenen Problem.
    Um Deadlocks zu vermeiden gibt es drei Ansätze:
    - Erst alle benötigten Objekte sperren (z.B. gleich Schreibsperren anfordern SELECT FOR UPDATE), dann darauf arbeiten und dann alle Ressourcen freigeben (Two phase commit)
    - Die Objekte immer in der gleichen Reihenfolge sperren/anfordern soweit möglich (z.B. alphabetische Reihenfolge ihrer Namen damit beide XActs in der selben Reihenfolgen Tabelle A, dann Tabelle B anfordern)
    - Warten, die DB sollte zyklische Abhängigkeiten erkennen und eine Transaktion zurückrollen



  • witte schrieb:

    - Erst alle benötigten Objekte sperren (z.B. gleich Schreibsperren anfordern SELECT FOR UPDATE), dann darauf arbeiten und dann alle Ressourcen freigeben (Two phase commit)
    - Die Objekte immer in der gleichen Reihenfolge sperren/anfordern soweit möglich (z.B. alphabetische Reihenfolge ihrer Namen damit beide XActs in der selben Reihenfolgen Tabelle A, dann Tabelle B anfordern)

    Die beiden Sachen sind aber schwierig, wenn man Tabellenzugriffe in bestimmte Klassen gekapselt hat. Da müsste man immer noch was umfassendes außenrum bauen und sich da genau überlegen welche Tabellen man braucht.



  • Es ist eher selten, dass Deadlocks auftreten. In dem meisten Situationen wird optimistisch gesperrt. Die Daten werden aus der DB gelesen und erst beim Einbringen der Änderungen durch ein Prüflesen gesichert. Man müßte jetzt genau Deine Anforderungen kennen und was Du machen willst um genau antworten zu können.
    Du kannst Dir auch mal MVCC anschauen:
    http://de.wikipedia.org/wiki/Multiversion_Concurrency_Control



  • Öh, sperren? Ist das nicht Aufgabe der Datenbank? Die sperrt sowieso keine kompletten Tabellen, sondern nur die benötigten (noch nicht committeten) Reihen. Wäre doch ein ziemlicher Overkill die komplette Tabelle zu sperren, nur weil sich eine Handvoll Datensätze gerade ändern. Bei 'ner Datenbank mit 'ner Handvoll Benutzer ok, aber für ernsthaftes Arbeiten? Never.

    Selbst SQLite ist mit 3.0 vom Table-Locking abgekommen. Wer lockt denn noch komplette Tabellen?



  • frenki schrieb:

    Öh, sperren? Ist das nicht Aufgabe der Datenbank? Die sperrt sowieso keine kompletten Tabellen, sondern nur die benötigten (noch nicht committeten) Reihen.

    Was das Problem nur auf nen kleineren Bereich verschiebt, aber nicht löst.



  • frenki schrieb:

    Selbst SQLite ist mit 3.0 vom Table-Locking abgekommen. Wer lockt denn noch komplette Tabellen?

    Dann erklär mir mal wie Du ALTER TABLE in einer Transaktion sichern willst.



  • Ehrlichgesagt habe ich keine Ahnung 🙂 Ich weiss das meine Datenbank im Gegensatz zu Oracle auch ALTER TABLE sowie CREATE TABLE etc Transaktionsbasiert durchführt (werden bei einem Rollback also genauso rückgängig gemacht wie ein INSERT). Aber wie das im Detail gemacht wird... Keine Ahnung. Ich weiss wie der Ablauf bei normalen Statements (insert, update, delete) vom Parsing bis zur Platte ist. Aber bei DDL? Da habe ich mich nie mit befasst.

    Aber solche Sachen macht man ja nicht im täglichen Betrieb sondern einmalig bei der Installation der Software, bzw. später bei Updates. Ich denke, hier geht es eher um das tägliche Arbeiten mit der Datenbank, also insert, update, delete.

    Und da wäre es doch ziemlich behämmert, z.B. die komplette Kundentabelle zu locken, nur weil ein Praktikant Listen mit Neukunden erfasst.



  • Die Datenbank lockt was sie für sinnvoll hält.
    Manche Datenbanken locken generell sehr wenig, andere wieder sehr viel. Kommt auch oft auf die verwendeten Einstellungen an.

    Wenn man z.B. "snapshot isolation" verwendet wird fast nix gelockt. Dafür kann es halt schneller passieren dass ein commit dann in die Hosen geht, und man retries fahren muss.

    Wenn man dagegen "serializable" (MS-SQL) verwendet wird sehr viel gelockt.

    Damit dass eine Transaktion wegen Deadlock/Timeout oder sonstwas abgebrochen wird muss man aber immer rechnen, d.h. man muss dann irgendwo einen retry Mechanismus vorsehen, damit das abgefangen wird.



  • Es ist immer wieder faszinierend das jemand eine allgemein gültige Aussage für eine Anwendung möchte obwohl man die Anforderung nicht kennt.
    Dies ist bei Datenbanken nicht möglich.
    Hier kommt es sogar auch auf das RDBMS an welches man verwendet.
    Und hierbei sogar auch noch auf die Version.
    Und wenn man tiefer geht sogar auf die Verwendung welches Tabellenformat (MySQL)
    Gibt es nur einen Client dann könnte jeder Thread eine eigene Verbindung haben.
    Gibt es viele Threads sollte man dann aber auch nicht das Locking der DB überlassen wenn man die Threads Sync. kann.
    Lesen auf die DB ist eher unkritisch.
    DELETE , UPDATE, INSERT eher dann mit einer Verbidung und jeder Thread kann lesen.
    Auch beim lesen kommt es dann darauf an wie man es macht.
    MySQL mit mysql_use_result() z.B. killt die Performance wenn es viele machen.
    Die Ergebnismenge bleibt am Server.

    Auch kommt es auf die Verwendetet Sprache an.
    NET hat sowieso Connectionpool.

    Ich würde sagen:
    Wenn du wenige Clients hast und wenige Threads hast dann jeder Thread eine Connection.
    Viele Clients dann pro Client eine oder 2 Con. (Select und INSERT/UPDATE/DELETE)
    u.s.w.

    Letzten Endes kommt es auch auf die DB-Hardware an.


Anmelden zum Antworten