Freitag, November 16, 2012

SQL Performance Explained von Markus Winand

Dass ich gerne mal ein Buch über Indizes von Richard Foote hätte, habe ich wahrscheinlich gelegentlich schon mal erwähnt, aber leider scheint damit auch weiterhin nicht zu rechnen zu sein - zumal die Herr Foote niemals versprochen hat, etwas Derartiges zu veröffentlichen. Stattdessen habe ich dieser Tage den im Sommer 2012 erschienenen Band SQL Performance Explained von Markus Winand gelesen, auf dessen interessante Seite Use The Index, Luke! ich hier auch schon verwiesen habe. Im ersten Moment ist es etwas ungewohnt, über Indizes zu lesen, ohne regelmäßigen Referenzen auf das Werk David Bowies zu begegnen, aber daran gewöhnt man sich ziemlich schnell ... 

Um es vorweg zu nehmen: das Buch ist aus meiner Sicht eine ausgesprochen empfehlenswerte Lektüre und liefert einen sehr zugänglichen Einstieg ins Thema SQL-Performance-Optimierung. Dabei wendet sich der relativ schmale Band (196 S.) in erster Linie an die Entwickler, die der Autor als die Gruppe betrachtet, die aufgrund ihrer Kenntnis der Applikationen (und - hoffentlich auch - der Daten) am besten dazu in der Lage ist, eine sinnvolle Indizierung durchzuführen, während DBAs und externen Beratern dieses Wissen in der Regel fehlt. Ich will an dieser Stelle nicht massiv widersprechen, denke aber, dass man viele SQL-Zugriffsprobleme auch ohne Kenntnis der Applikationslogik bestimmen kann (jedenfalls in Oracle und im SQL Server, da für diese RDBMS gilt, dass das data dictionary und die dynamischen Performance-Views sehr viele relevante Informationen liefern). Dass die Entwickler ein gutes Verständnis der Arbeitsweise von Indizes haben sollten, stimmt aber in jedem Fall. 

Das Thema des Buches sind B*Tree-Indizes und ihre Rolle in OLTP-Systemen. Diese starke Fokussierung auf eine zentrale - und beschränkte - Fragestellung und eine klare Strukturierung der Erklärungen sorgen dafür, dass die Darstellung sich nicht in Details verliert. Diese Struktur leidet auch nicht darunter, dass die Erläuterungen nicht auf ein einziges RDBMS beschränkt sind - neben Oracle und SQL Server werden MySQL und PostgreSQL untersucht -, im Gegenteil: durch den Vergleich der Systeme wird deutlich, wie viele Übereinstimmungen es in den grundsätzlichen Verfahrensweisen der Datenbanken im Bereich der Indizierung und der SQL-Optimierung gibt. Das Buch gliedert sich in acht Kapitel:
  • Anatomy of an Index: erläutert die Struktur von B*Tree-Indizes.
  • The Where Clause: erklärt die Rolle unterschiedlicher Operatoren (Equality, Range), Funktionen (und FBIs), NULL-Werten, Datentypen, Statistiken, Bindewerten und liefert dabei zahlreiche Antworten auf die klassische Frage, warum ein Index nicht verwendet wird. Einer der wichtigsten Punkte ist aus meiner Sicht die prägnante Erklärung von access und filter Prädikaten. Nützlich sind auch die Hinweise auf das unterschiedliche Verhalten unterschiedlicher RDBMS (z.B. Oracles fragwürdige Behandlung von Leerstrings als NULL).
  • Performance und Scalability: zeigt den Einfluss von Datenvolumen und Contention auf die Performance.
  • The Join Operation: behandelt die drei Join-Verfahren (Nested Loops, Hash Join, Merge Join) und ihre Nutzung von Indizes. Dabei wird auch das Thema der Code-Generierung von ORM-Tools angesprochen und vorgeführt, wie man deren traurige Leistungen in bestimmten Fällen korrigieren kann.
  • Clustering Data: erklärt den clustering factor und die Leistungsfähigkeit von index-only scans (covering indexes; "the second power of indexing"); außerdem wird die Struktur von IOTs (bzw. clustered indexes) erläutert. 
  • Sorting and Grouping: erklärt die Möglichkeiten zur Vermeidung von Sortierungen bei ORDER BY und GROUP BY Operationen durch die Nutzung geeigneter Indizes ("the third power of indexing", wobei die Verarbeitung "pipelined" erfolgt: der nächste Verarbeitungsschritt muss also nicht das Ende der Sortierung abwarten). Außerdem werden die Sortierreihenfolge (ASC, DESC) und die Position von NULL-Werten (FIRST, LAST) beim Sortieren thematisiert.
  • Partial Results: zeigt effiziente Verfahren zur Ausgabe paginierter Ergebnisse und geht (knapp) auf analytische Funktionen ein.
  • Modifiying Data: erklärt die Wirkung von Indizes auf DML-Operationen.
  • Appendix A: mit Hinweisen zur Darstellung und Interpretation von Ausführungsplänen in den behandelten RDBMS.
Ohne jeden Zweifel kennt der Autor seine Materie sehr genau - und ist dazu in der Lage, sie zu vermitteln.  Dabei bleibt die Darstellung nicht bei Behauptungen, sondern führt die angesprochenen Effekte immer wieder an praktischen Beispielen vor (sehr häufig sind das Ausführungspläne). In einigen Fällen dienen übersichtliche Grafiken zur Visualisierung von Zusammenhängen (Struktur von Branch- und Leaf-Knoten). Ein häufiges Phänomen bei meiner Lektüre war der Gedanke: da fehlt aber noch der Hinweis auf Effekt xyz (z.B. bind peeking, adaptive cursor sharing), der dann mit schöner Regelmäßigkeit wenige Seiten später erschien: aus didaktischer Sicht ist das wahrscheinlich günstig: zuerst wird das grundlegende Phänomen dargestellt, die Spezialfälle kommen dann mit einem gewissen Abstand. Ein anderer Punkt, der mir gut gefällt, ist der Hinweis auf einige klassische Mythen der Indizierung, z.B. auf die "unbalanced trees", die man durch regelmäßigen Rebuild bei Laune halten muss (aus Gründen der Deutlichkeit: es gibt keine "unbalanced trees" in b(alanced)*Tree-Indizes; und ein Index-Rebuild ist nur in sehr wenigen - und klar bestimmbaren - Fällen nützlich, auch wenn auf gewissen Seiten, die bei der Google-Suche häufig ganz oben erscheinen, etwas anderes behauptet wird oder wurde). Zu den Qualitäten des Buchs gehört auch die sprachliche Klarheit und pointierte Darstellung (wichtige Punkte werden als Merksätze grafisch hervorgehoben), wobei ich die englische Version gelesen habe, aber keinen Grund habe anzunehmen, dass Gleiches nicht auch für die deutsche Version gilt.

Gut gefällt mir wohl auch, dass die Einschätzungen des Autors in nahezu allen wichtigen Punkten mit den meinen übereinstimmen. Ein Punkt, den ich vielleicht anders akzentuieren würde, ist die Rolle von Bindewerten: natürlich sind sie in OLTP-Systemen zur Vermeidung von contention extrem wichtig, aber andererseits nehmen sie dem Optimizer relevante Informationen. Da ich mich aber auch eher mit ETL-Fragen im DWH-Kontext beschäftige, lässt sich dieser Aspekt vermutlich ziemlich schnell abhaken (ich glaube, das ist ein Punkt in dem auch die Propheten Kyte und Lewis leicht abweichende Positionen einnehmen). Ein paar kleinere Details habe ich in den Ausführungen vermisst (z.B. den INDEX SKIP SCAN, obwohl, so richtig vermisse ich den eigentlich nicht; den FIRST_ROWS_n-Modus für den CBO; den rowid-guess in IOTs und deren Overflow-Segment), aber das Erstaunliche ist viel mehr, was hier alles auf weniger als 200 Seiten angesprochen wird. Eine Frage, die mich noch interessieren würde, wäre, wo um alles in der Welt man Indizes mit einer tree depth von 6 findet? (mehr als 4 habe ich auch auf relativ großen Tabellen mit mehreren Milliarden Sätzen noch nicht gesehen, aber vielleicht ist das jenseits der Oracle-Welt anders)

Ich denke, dass SQL Performance Explained ein ungeheuer nützliches Buch für jeden ist, der beginnt, sich ernsthaft mit Fragen der SQL-Optimierung auseinander zu setzen - und das sollte aus meiner Sicht eigentlich jeder Entwickler, der SQL-Code schreibt. Im Bereich der SQL-Zugriffe lassen sich Laufzeiten häufig um Größenordnungen reduzieren, wenn man den richtigen Index benutzt (bzw. im DWH-Kontext eher: nicht benutzt, denn dort sind es mir schöner Regelmäßigkeit die Index-getriebenen NL-Joins, die zu Problemen führen) - um solche Verbesserungen in anderen Teilen des Codes zu erreichen, muss man sich schon sehr viel einfallen lassen. Selbst, wenn man sich schon länger mit Fragen der SQL-Optimierung beschäftigt, wird man hier noch allerlei nützliche Hinweise finden: für mich waren das vor allem die Erläuterungen zum Verhalten anderer RDBMS, mit denen ich seltener zu tun habe (SQL Server), bzw. fast nie (MySQL, PostgreSQL). Auch habe ich mir noch nie ernsthaft darüber Gedanken gemacht, dass Indizes auf einer SQL Server-Tabelle mit clustered index notwendigerweise die gleichen Probleme haben wie sekundäre Indizes auf IOTs. Ich kenne kein anderes Buch, dass die Grundlagen der SQL-Performance-Optimierung ähnlich gut erläutern würde (vielleicht am ehesten Christian Antogninis Troubleshooting Oracle Performance, das allerdings ein größeres Vorwissen voraussetzt und auch Aspekte anspricht, die eher in den DBA-Bereich fallen). Würde ich in diesem Blog Kaufempfehlungen aussprechen, dann wäre SQL Performance Explained ein Kandidat für eine solche.

Kommentare:

  1. Indextiefe ist auch von der Breite des Indexes abhängig. Ich habe gerade einen Index mit 9Mio Records auf Leaf-Ebene und avg_record_size_in_bytes=108 bytes vor Augen. Die Indextiefe ist 5.
    Ein Beispiel wäre ein NonClustered Index auf einer sehr breiten (und relativ großen) Dimensionstabelle, in dem man ein Viertel der Tabellenpalten INCLUDED hat (da man davon ausgeht, dass beim Zugriff über diesen Index nur diese Spalten benötigt werden, und man damit den Zugriff auf die Basistabelle komplett vermeiden will).
    Man könnte das mit Sicherheit auch anders lösen, aber ganz realitätsfern ist das Szenario nicht.

    SELECT OBJECT_NAME(P.object_id) AS 'Table'
    , I.name AS 'Index'
    , P.index_id AS 'IndexID'
    , P.index_type_desc
    , P.index_depth
    , P.page_count
    , P.avg_record_size_in_bytes
    , P.record_count
    FROM sys.dm_db_index_physical_stats (DB_ID(),
    NULL,
    NULL, NULL, 'DETAILED') P
    JOIN sys.indexes I ON I.object_id = P.object_id
    AND I.index_id = P.index_id
    WHERE P.index_depth>4

    AntwortenLöschen
  2. Hallo Andrej,

    schön von Dir zu hören. Über die INCLUDING option hatte ich in diesem Zusammenhang noch gar nicht nachgedacht. Die Breite des Index ist sicher der entscheidende Faktor: offenbar hat's mir da in der Vergangenheit einfach an Phantasie gefehlt...

    Gruß + schöne Feiertage

    Martin

    AntwortenLöschen