Linuchs: mySQL locking table / record - Problem mit MS Access

Moin,

„Die Sicherheitsupdates vom 14. Dezember 2021 für Microsoft Office (MSI-Installer-Version) verursachen Probleme mit Microsoft Access. Es kann nur noch ein Nutzer auf die Datenbanken zugreifen. … Das betrifft auch die Access 2013/2016 Runtime, die von manchen Nutzern eingesetzt wird.“ Quelle

Betrifft mich unter Linux nicht, aber ich möchte dieses Lock-Problem verstehen, da ich damit noch nie gearbeitet habe.

Aus ferner Vergangenheit (Oracle DB) kenne ich, dass ein Satz zum Ändern gelesen wird. Er wird also eine undefiniert lange Zeit „geschützt“ gegen Änderungen / Löschungen durch andere User *). Bei Programmfehler kann die „Freigabe“ vergessen werden.

Wie ist das, wenn ein Satz mit phpMyAdmin neu angelegt wird? Wird da so ein Lock gesetzt und vergessen, den zurückzunehmen? Und bei Änderungen eines Satzes?

Gruß, Linuchs

*) Wie, wenn ich - als derselbe User - diesen Satz in zwei Fenstern bearbeite? In der DB habe ich mich ja mit demselben User angemeldet.

  1. Hallo Linuchs,

    Aus ferner Vergangenheit (Oracle DB) kenne ich, dass ein Satz zum Ändern gelesen wird. Er wird also eine undefiniert lange Zeit „geschützt“ gegen Änderungen / Löschungen durch andere User

    Ein Update muss vorher lesen, sonst könntest Du kein "SET col = col+1" oder so machen. Und ein Update hinterlässt einen Write-Lock auf der Row, so dass sie kein anderer mehr schreiben kann.

    Aber auch ein SELECT kann einen Lock hinterlassen, einen Read-Lock. Das hängt vom Isolation Level ab und davon, ob eine Transaktion begonnen wird. Ohne Transaktion wird meines Wissens keine Lesesperre gesetzt. Die Isolation ist per Default relativ hoch, man kann das aber auch absenken.

    Ein Read-Lock kann vom gleichen Programm zum Write-Lock erhoben werden, aber ein zweites Programm bekommt maximal einen weiteren Read-Lock. Ein Write-Lock kann nur erzeugt werden, wenn es keine Read-Locks gibt. Aber, wie gesagt, die genauen Regeln hängen vom Isolation Level ab.

    Die Lesesperre endet mit der Transaktion, also mit einem expliziten COMMIT bzw. ROLLBACK, oder einem impliziten Transaktionsende bei Ende des Programms. Da gibt's viel Konfigurationsspielraum.

    Eine Write-Lock verhindert normalerweise auch, dass der Satz von anderen gelesen wird. Es sei denn, sie machen einen "uncommitted read", dann geht's wieder.

    Wie das bei Access läuft, ist eine andere Frage und die kann ich nicht beantworten.

    Rolf

    --
    sumpsi - posui - obstruxi
    1. Hallo Rolf,

      Ein Update muss vorher lesen, sonst könntest Du kein "SET col = col+1" oder so machen.

      Das ist klar, aber zu spät. Ich hole mir Treffer per SELECT (durchaus kombiniert aus mehreren Tabellen), zeige die an zum Ändern.

      Jetzt muss die Zeit laufen, dass kein anderer User den Satz / die Sätze zum Ändern oder Löschen angeboten bekommt. Lesen sollte kein Problem sein, das wäre ja der aktuelle Stand vor dem Ändern.

      Als ich 2001 mit mySQL begann, waren LOCK-Funktionen, Views usw. nicht möglich und ich hatte diese DB mehr als PC-Spielzeug gesehen wie Plastik-Werkzeug für Kinder und erst im Lauf der Jahre Achtung bekommen, was FÜR UMME eine ansehnliche Leistung geboten wird. Aber das Oracle-Know-How war inzwischen verblasst und es ging ja auch ohne.

      So, inzwischen hat der User sich einen Kaffee geholt, hat telefoniert, war zum Mittag Essen und auf Klo und schickt „seine/n“ reservierten Satz / reservierte Sätze ändernd zurück.

      Die ganze DB minuten- oder stundenlang gegen andere Änderer zu sperren kann ja nicht funktionieren. Wie also ist das Konzept?

      Gruß, Linuchs

      1. Hallo Linuchs,

        was Du suchst, heißt "optimistisches Sperren". Dafür brauchst Du in den Sätzen, die Du ändern willst, einen "Last Update" Timestamp. Bei jedem Update änderst Du den.

        Beim Einlesen:

        SELECT a, b, c, lastupdate
          FROM somedata 
         WHERE id=:idValue
        

        Beim Update

        UPDATE somedata
           SET a = :aValue, b = :bValue, c = :cValue
        WHERE id = :idValue
          AND lastupdate = :readTimestamp
        

        Und dann bekommst Du "0 Sätze geändert", wenn sich zwischenzeitlich was geändert hat. Damit das klappt, konfigurierst Du die Table so, dass die lastupdate Spalte vom Typ TIMESTAMP ist und nach jedem Update den aktuellen Timestamp annimmt. Ich hoffe, das funktioniert schon mit deinem Uralt-Mysql (du hast noch 5.5, wenn ich mich recht erinnere). Andernfalls musst Du den Timestamp bei jedem Update manuell auf NOW() setzen.

        Hier steht, wie.

        Anstelle eines TIMESTAMP kannst Du auch einfach einen INT nehmen und den bei jedem Update inkrementieren. Eine "Satzversion", sozusagen. Falls Du dabei einen INT-Überlauf befüchten musst, weil es für einen Satz drölftausend Änderungen pro Tag geben könnte, inkrementiere ihn modulo 1000000 oder so.

        Rolf

        --
        sumpsi - posui - obstruxi
        1. Hallo Rolf,

          Falls Du dabei einen INT-Überlauf befüchten musst, ...

          muss er nicht, den kann er ganz entspannt einfach geschehen lassen.
          Ein Integer-Überlauf ist völlig gleichgültig, solange man nur zwei Werte auf Gleichheit prüft. Bei größer/kleiner-Relationen sieht das wieder anders aus.

          Immer eine Handbreit Wasser unterm Kiel
           Martin

          --
          The taste of love: The more you get, the more you want
          (aus The Lightning Seeds: Sense)
          1. Hallo Martin,

            MYSQL ist nicht C. Ich weiß nicht, was der Server tut, wenn man auf eine INT-Column, die den Wert $$2^{31}-1$$ enthält, eins aufaddiert. Gibt es einen SQL Error, wird es ignoriert oder läuft er automatisch nach $$-2^{31}$$ über?

            Wenn er überläuft, ist das definiertes oder implementationsabhängiges Verhalten?

            Das habe ich nicht im Kopf und keine Zeit, es zu recherchieren.

            Rolf

            --
            sumpsi - posui - obstruxi
  2. Wo - in mySQL, MS Access oder beiden - wird vermerkt, welche Teile der Datenbank gegen Änderung gesperrt sind?

    Oder andersrum: Wenn MS Access neu gestartet wird, „erinnert“ es sich daran, dass was gesperrt war?