DBAtlas · Atlas für Datenbankbetrieb

Langsame Schreibvorgänge (INSERT/UPDATE-Latenz)

INSERT/UPDATE/DELETE-Operationen sind langsamer als erwartet. Zuerst bestimmen wir, worauf der Schreibvorgang wartet (Commit/Redo, Sperren, Disk oder Overhead pro Zeile), und arbeiten uns dann Schritt für Schritt zur Ursache vor.

Worauf wartet der Schreibvorgang?

Klassifiziere den Engpass anhand der Wait Events: Dominiert die Commit-/Redo-Synchronisierung (WAL), Sperren, die Disk-Schreiblatenz oder Index-/Trigger-Overhead pro Zeile?

SELECT event, total_waits, ROUND(time_waited/100,1) AS sec_waited
FROM v$system_event
WHERE event IN ('log file sync','log file parallel write','enq: TX - row lock contention','db file sequential read')
ORDER BY time_waited DESC;
  • Wartet auf Commit / Redo (WAL-Sync)
  • Wartet auf eine Sperre
  • Vermuteter Overhead pro Zeile (Indizes/Trigger)
  • Disk-Schreiblatenz ist hoch

Commit-Muster oder fsync?

Ein eigener Commit pro Zeile (Autocommit) löst die Redo-/Log-Synchronisierung so oft aus, wie es Zeilen gibt. Ist umgekehrt die Commit-Anzahl vernünftig, jeder Commit wartet aber auf fsync, liegt das Problem bei der Latenz der Log-Disk.

SELECT name, value FROM v$sysstat WHERE name IN ('user commits','redo writes','redo size');
SELECT ROUND(time_waited_micro/GREATEST(total_waits,1)/1000,2) AS ms_per_sync FROM v$system_event WHERE event='log file sync';
  • Autocommit / ein Commit pro Zeile
  • Commit wartet auf Log-Synchronisierung (fsync)

Wer hält die Sperre?

Wartet der Schreibvorgang auf eine Sperre: Eine andere Session hält vielleicht dieselbe Zeile/denselben Index, oder viele Sessions versuchen, in dieselbe „heiße“ Zeile zu schreiben.

SELECT s.sid, s.serial#, s.username, s.blocking_session, s.event, s.seconds_in_wait
FROM v$session s WHERE s.blocking_session IS NOT NULL;
  • Eine andere Session hält die Sperre
  • Viele Sessions schreiben in dieselbe heiße Zeile

Woher kommt der Overhead pro Zeile?

Jedes INSERT/UPDATE aktualisiert auch alle Indizes der Tabelle und löst etwaige Trigger/Constraints aus. Eine übermäßige Anzahl von Indizes oder ein schwerer Trigger verlangsamt Schreibvorgänge erheblich.

SELECT index_name, uniqueness FROM user_indexes WHERE table_name='T';
SELECT trigger_name, triggering_event FROM user_triggers WHERE table_name='T';
  • Die Tabelle hat zu viele / unnötige Indizes
  • Es gibt hohe Kosten durch Trigger oder Constraints

Ursache: Autocommit / Commit pro Zeile

Ein eigener Commit pro Zeile wiederholt die Redo-/Log-Synchronisierung so oft, wie es Zeilen gibt. Durch Bündeln der Operationen kannst du die Anzahl der Commits (und damit der fsyncs) drastisch senken.

Ursache: langsame Redo-/Log-Disk (fsync-Latenz)

Jeder Commit wartet, bis der Redo-/WAL-Eintrag auf dauerhaften Speicher geschrieben ist (fsync). Ist die Log-Disk langsam (hohe Schreiblatenz, unzureichende IOPS), zahlt jeder Commit diese Latenz.

Ursache: Eine andere Session hält die Sperre

Der Schreibvorgang wartet auf eine Zeilen-/Indexsperre, die eine andere Session hält. Der Blocker ist meist eine lang laufende Transaktion oder eine, deren Commit vergessen wurde.

Ursache: Hot-Row-Konkurrenz

Viele Sessions versuchen, in dieselbe Zeile zu schreiben (z. B. Zähler, Sequenznummer, Saldo); alle stehen für dieselbe Sperre an und Schreibvorgänge werden serialisiert.

Ursache: übermäßiger Index-Overhead

Jeder Index der Tabelle wird bei jedem Schreibvorgang aktualisiert. Viele oder ungenutzte Indizes verlangsamen INSERT/UPDATE direkt.

Ursache: schwerer Trigger / schwere Constraint

Ein Trigger, der bei jedem Schreibvorgang feuert, oder eine zu prüfende Constraint (insbesondere eine Fremdschlüsselprüfung ohne Index) verursacht bei jeder Zeile zusätzliche Kosten.

In DBAtlas öffnen →