Archiv der Kategorie: Datenbanken

MS-SQL-Server: Schlechte DELETE-Performance

Ein Kollege meldete sich vor einigen Wochen mit einem interessanten Problem: Beim Löschen von Objekten aus einem komplexeren und gut befüllten Datenmodell liefen alle DELETEs in den Subtabellen und Kreuzreferenzen mit der erwarteten Geschwindigkeit (also im niedrigen Millisekundenbereich) ab, aber die DELETEs auf der Haupttabelle benötigten je Objekt dann plötzlich einige Sekunden.

Mehrere Stunden brüteten wir zusammen mit Kollegen über dem Problem, untersuchten den Ausführungsplan der DELETE-Anweisung, misteten Indizes aus, erstellten andere Indizes neu, reorganisierten die betroffene Tabelle… alles ohne Ergebnis.

Erst ein weiterer Blick auf den Ausführungsplan, diesmal ohne Beachtung der angeblichen Aufwandsverteilung, führte dann zur Lösung: In einer anderen Tabelle mit mehreren Millionen Einträgen befand sich eine Fremdschlüsselspalte, die jedoch nicht Indiziert war. Folglich musste für jedes DELETE eine Full-Table-Scan ausgeführt werden um eventuelle Abhängigkeiten auszuschließen.

Daher nun die kurze Merkregel:

Lege auf einer Fremdschlüsselspalte immer auch einen Index an (sofern sie nicht schon führend in einem anderen Index enthalten ist). Sind in der Tabelle nur wenig Daten, dann tut der zusätzliche Index nicht weh, sind in der Tabelle jedoch viele Daten, dann wird man spätestens beim DELETE in der referenzierten Tabelle froh um den Index sein.

Und wie immer: Trotz Merkregel kann es in Einzelfällen gute Gründe geben, sich anders zu verhalten.

“Casting-Show” mit dem MS SQL Server – Teil 2

Vor gut zwei Monaten hatte ich über unterschiedliche Ergebnismengen bei prepared vs. non-prepared Statements berichtet. Zwischenzeitlich kam ein weiteres Phänomen dazu, was an das erste Phänomen erinnert:

Seit mehreren Monaten fallen uns immer mal wieder prepared Queries auf, deren Performance deutlich (Faktor 10 bis 100) schlechter ist, als das entsprechende non-prepared Queries. Alle Queries hatten gemeinsam, dass in einer wichtigen Bedingung eine VARCHAR-Spalte verwendet wird, auf der es einen Index gibt. Ein Blick auf die Ausführungspläne zeigte, dass die non-prepared Query einen performanten Index-Lookup durchführte, die prepared Query jedoch einen langsamen Index-Scan.

Als Ursache stellte sich der als Unicode-Wert übergebene Parameter der Bedingung auf der VARCHAR-Spalte (also keine Unicode-Werte) heraus. In so einem Fall scheint der SQL-Server – warum auch immer – keinen Lookup im Index machen zu wollen.

Lösungen:

  1. Ein Umstieg auf NVARCHAR-Spalten. Das ist unser bevorzugtes, mittelfristiges Ziel aber wie immer im Leben leider nicht sofort umsetzbar.
  2. Den JDBC-Treiber auf Non-Unicode-Parameterübergabe umschalten (sofern möglich).

„Casting-Show“ mit dem MS SQL Server

Eigentlich sollte man meinen, dass mit Unicode diverse Probleme rund um das Thema Sonderzeichen endlich gegessen wären, aber leider durfte ich neulich erleben, dass dem nicht so ist.

Gegeben sei eine einfache Tabelle „test“ mit einer NVARCHAR(50)-Spalte „name“ und zwei Datensätzen: „Preussen“ und „Preußen“.

Eine ebenso einfache Abfrage

SELECT * FROM test WHERE name = 'Preussen'

liefert – wie man es erwartet – einen Treffer. Macht man aus Ihr ein Prepared Statement, dann sind es plötzlich zwei Treffer: „Preussen“ und „Preußen“.

Natürlich sucht man den Fehler nun erstmal im eigenen Code, findet aber nichts, also probiert man alternative JDBC-Treiber aus und… findet auch nichts, bis man das ganze zum Schluss im Management Studio versucht und auch dort das seltsame Verhalten reproduzieren kann. Erst ein expliziter CAST des Vergleichswertes nach VARCHAR behebt das Problem. Ob diese Lösung aber performanter ist als der Verzicht auf Prepared Statements habe ich noch nicht geprüft.