Dieser Artikel gibt Ihnen eine kompakte Anleitung zur effektiven Nutzung der Rushmore-Optimierung in SQL-Abfragen und Suchprozessen jeglicher Art. Anhand der enthaltenen Checkliste können Sie durch Ihre Programme gehen und Performance-Reserven aufdecken. Die Regeln wurden unter MS FoxPro entwickelt, dem Ursprung der Rushmore-Optimierung. Sie gelten aber mit hoher Wahrscheinlichkeit auch für die neuen Versionen von MS Access 2.0 bzw. für MS Visual Basic beim Zugriff auf die neue Access-Engine, da dort die Rushmore-Optimierung integriert wurde.
Der Projektname "Rushmore" wurde von dem Team bei Fox Software nach dem Anschauen des Hitchcock-Klassikers "Der Unsichtbare Dritte" vergeben. In einer Filmszene war der Präsidentenhügel Mount Rushmore zu sehen und wurde so zum Namensgeber. Hinter dieser einprägsamen Bezeichnung verbirgt sich eine Zugriffsoptimierung beim Arbeiten mit Gruppen von Datensätzen. Alle größeren SQL-Datenbanksysteme haben ebenfalls eine integrierte Optimierung von Abfragen, einen sogenannten Query Optimizer - diese ist aber mit Rushmore nur begrenzt vergleichbar. In der Großrechnerwelt ist diese Form der Optimierung schon länger bekannt, aber Rushmore ist die erste Implementation im PC-Bereich. Die Rushmore-Optimierung ist nicht auf SQL-Abfragen oder Client/Server begrenzt, sondern unterstützt alle Arten von gruppenbezogenen Zugriffen - und das schon auf kleinen Einzelplatzsystemen unter DOS, Windows oder Macintosh.
Das Lieblingsbeispiel von Microsoft ist das Durchsuchen des amerikanischen Straßenverzeichnisses nach allen Straßen mit dem Beginn "Sunset". Es werden 5000 Straßen aus ca. 2,8 Millionen Datensätzen binnen 0,66 Sekunden selektiert. Die Beschleunigungseffekte von Rushmore sind häufig erstaunlich. Vorausgesetzt natürlich, es steht ausreichend Hauptspeicher zur Verfügung und die Regeln für die Rushmore-Optimierung wurden eingehalten. Der Hauptspeicher wird zur Erstellung einer Bitmap benötigt, pro ca. 500.000 Datensätze wird ein Block von 64 Kilobyte benötigt.
Grundregel für Rushmore ist: Beschleunigt werden nur Zugriffe auf Datensatzgruppen, nicht Zugriffe auf einzelne Sätze á la SEEK oder auf die Gesamtdatenbank. Optimiert werden Vergleiche in der Suchbedingung mit den Operatoren gleich, größer als und kleiner als bzw. Kombinationen davon incl. Negierungen. Von Rushmore profitieren keine Suchbedingungen mit exakten Vergleichen oder mit bestimmten Funktionsaufrufen (z.B. EMPTY() oder SUBSTR()). Es können auch mehrere Suchbedingungen mit AND und OR verknüpft werden. Sofern nicht alle Teile des Gesamtausdrucks optimierbar sind, erhält man zumindest noch eine teiloptimierbare Abfrage. Problematisch wird nur die Verknüpfung von optimierbaren und nicht optimierbaren Ausdrücken über die Oder-Funktion - dann ist die Gesamtabfrage nicht mehr optimierbar und der Geschwindigkeitsvorteil ist wieder mal dahin.
Die Optimierung von Rushmore findet nicht nur bei SQL-Abfragen statt. Alle Operationen, die eine Gruppe von Datensätzen betreffen, werden beschleunigt. Unter FoxPro sind dies Befehle wie REPLACE (Feldwerte ersetzen), DELETE (Löschen), BROWSE (Sätze als Tabelle anzeigen) oder auch der Schleifenbefehl SCAN (Schleife über Datenbanksätze), sofern sie sich nicht auf einen einzelnen Satz oder auf die Gesamtdatenbank beziehen sondern durch eine FOR-Klausel (ähnlich dem WHERE bei SQL) die betroffenen Datensätze einschränken. Da für den Einsatz der Rushmore-Technologie ein Satz von Regeln gilt, ohne deren Kenntnis häufig nicht der gewünschte Beschleunigungseffekt erreicht wird, haben wir nachfolgend die wichtigsten Optimierungsregeln aus verschiedenen Quellen zusammengetragen:
Die Zugriffsoptimierung erfolgt hauptsächlich auf Basis der bestehenden Indizes. Alle Datenbankfelder, deren Inhalte häufiger zur Eingrenzung von Sätzen in Abfragen vorkommen, sollten mit einem Index versehen werden. Das kostet natürlich Speicherplatz und erhöht die Bearbeitungszeit beim Neuanlegen oder Zurückspeichern von Datensätzen geringfügig. Der Geschwindigkeitsvorteil durch Rushmore beim gezielten Lesen von Sätzen gleicht dies aber in der Praxis bei weitem wieder aus. Es müssen auch nicht wirklich alle jemals in Abfragen vorkommenden Felder indiziert werden - nur die wichtigsten. Dann hat man immerhin schon eine Teiloptimierung und diese reicht häufig schon für eine vernünftige Zugriffsgeschwindigkeit aus.
FoxPro arbeitet mit kompakten Mehrfach-Indexdateien mit der Extension CDX. Die komprimierte Form der Indexabspeicherung in CDX- oder kompakten IDX-Dateien führt zu einem schnelleren Indexzugriff, da weniger Sektoren gelesen werden müssen. Rushmore funktioniert aber auch mit anderen Formen von Indexdateien. Dies gilt natürlich nicht für Indexdateien von Client/Server-Datenbanken wie z.B. MS SQL Server, da in diesem Fall die Daten ja vom Host selektiert und an den Client nur durchgereicht werden.
Wichtigste Regel ist für Rushmore die exakte Übereinstimmung zwischen der Definition des Suchbegriffs und der Indexdefinition, wobei der Name des einzelnen Index keine Rolle spielt. Sofern keine Funktionen verwendet wurden, ist dies automatisch der Fall und die Suchbedingung "FOR/WHERE NAME1='BECKER'" kann über den Index auf das Feld "INDEX ON NAME1 TAG NAME1" optimiert werden. Gewöhnungsbedürftig hingegen ist die Schreibweise bei Zugriffen auf Feldern mit zusammengesetzten Indizes oder mit Funktionsaufrufen in der Indexdefinition. Habe ich mit "INDEX ON LAND+PLZ+ORT TAG WOHNORT" einen zusammengesetzten Index erstellt und möchte alle Schweizer Adressaten selektieren, lautet meine Abfrage "FOR/WHERE LAND+PLZ+ORT='CH'". Das sieht vielleicht witzig aus und ist etwas umständlich, weist aber Rushmore den Weg zur passenden Indexdefinition. Habe ich mit "INDEX ON UPPER(NAME1) TAG NAME1" einen Index nur mit Großbuchstaben erstellt, suche ich darin optimiert über "FOR/WHERE UPPER(NAME)='BECKER'" - die Variante "FOR NAME=UPPER('BECKER')" hingegen wird zu einer quälend langsamen Angelegenheit. Die Regel mit der Übereinstimmung von Indexdefinition und Suchausdruck gilt auch für numerische Felder. Addiert man bei der Indexdefinition z.B. eine Konstante "INDEX ON NUMMER+0 TAG NUMMER", so ist dies auch beim Zugriff anzugeben: "FOR/WHERE NUMMER+0 = 4711". Dabei spielt es keine Rolle, daß die Addition von Null keinen praktischen Nutzen hat - es ist nun mal die exakte Indexdefinition im Suchbegriff zu wiederholen.
Bei der Übereinstimmungsprüfung werden Negierungen nicht berücksichtigt. Sowohl "FOR DELETED()" als auch "FOR NOT DELETED()" sind bei Vorhandensein eines Index DELETED() optimierbar, da das NOT bei der Suche nach einem passenden Index ignoriert wird. Die Rushmore-Optimierung ist allerdings nicht in der Lage, mit eingeschränkten oder negierten Indizes zu arbeiten. Ein Index auf "NOT DELETED()" würde also in beiden Beispielen nicht erkannt und stattdessen die gesamte Datenbank sequentiell durchlaufen. Das gleiche gilt für einen eingeschränkten Index wie "DELETED() FOR DELETED()". Dieser Index würde zwar nur einen Bruchteil des Speicherplatzes beanspruchen, da ausschließlich die gelöschten Sätze indiziert würden - da im Gegenzug aber keine Optimierung mehr möglich ist, wäre dies ein schlechter Tausch. Wenn man mit Rushmore arbeiten möchte, sollte man also besser mehr Platz für uneingeschränkte Indexdefinitionen einkalkulieren.
Die Einstellung einer Satzreihenfolge hat keinerlei Effekt auf die Geschwindigkeit von SQL-Abfragen. Bei SQL-Abfragen wird die Reihenfolge durch die ORDER-BY-Klausel bestimmt, ggf. erstellt Rushmore für diesen Zweck einen temporären Index. Bei dem Setzen von Filterbedingungen (SET FILTER TO) hingegen macht sich das Setzen einer Reihenfolge sehr deutlich bemerkbar. Im Gegensatz zu anderen Xbase-Dialekten ist der Filter-Befehl bei FoxPro über Rushmore optimierbar und damit tatsächlich auch bei großen Datenbanken in der Praxis verwendbar. Nur eine Reihenfolge hebt diesen Beschleunigungseffekt fast bis zur Gänze wieder auf. Sofern man die Datensätze unbedingt in einer bestimmten Sortierung anzeigen möchte, sollte man also statt einem Filter eine SQL-Abfrage benutzen. Nicht ganz so deutlich wird der negative Effekt bei den normalen Befehlen, die mit Satzgruppen arbeiten. Generell gilt hier dennoch, daß der Zugriff ohne Einstellen einer Reihenfolge deutlich schneller erfolgt.
Optimiert werden können nur Befehle mit einer FOR-Klausel. Bei zusätzlicher Verwendung einer WHILE-Klausel wird die Optimierung abgeschaltet. Bei einer Bereichseingrenzung eines Befehls mit anderen Bereichen als ALL oder NEXT kann ebenfalls nicht optimiert werden. Ein Befehl wie REPLACE REST WHILE <Bedingung> ist also keinesfalls optimierbar.
Datenbanken im DBF-Format verwenden das erste Byte eines Datensatzes als logisches Löschkennzeichen. Die physische Löschung findet erst bei der nächsten Komprimierung der Datenbank statt. Da man normalerweise nur mit nicht gelöschten Sätzen arbeiten möchte, Schaltereinstellung SET DELETED OFF, führt dies zu einer internen Erweiterung der Zugriffsbedingung. Rushmore fügt die Bedingung ".AND. NOT DELETED()" zu der FOR- bzw. WHERE-Klausel hinzu. Die Lösung des Problems ist die Erstellung eines Index auf das Löschkennzeichen - dadurch ist auch die interne Zusatzklausel optimierbar. Der Befehl dazu lautet "INDEX ON DELETED() TAG DELETED". Wahlweise kann man natürlich auch gelöschte Sätze mit einbeziehen (SET DELETED OFF). Eine Beschleunigung von SQL-Abfragen bei größeren Datenmengen um 10% bis 20% ist garantiert.
Ein knappes Dutzend alternative Sortiersequenzen stehen für die Indexerstellung zur Verfügung. Vorgabewert ist die Sortierung "GENERAL", die zwischen Groß- und Kleinbuchstaben nicht unterscheidet und alle Umlaute dem Grundbuchstaben zuordnet (Ä wird zu A usw.). Jedem wird die Sortierfolge "MACHINE" bekannt vorkommen - hier werden die Zeichen nach dem ASCII-Wert sortiert, was Groß- und Kleinbuchstaben trennt und Umlaute für Anwender recht unerwartet einsortiert. Im deutschsprachigen Raum kann außerdem "UNIQWT" verwendet werden, die restlichen Sortierfolgen sind für Fremdsprachen gedacht. Der Programmierer sollte sich hier für eine Sortierfolge entscheiden und nach dieser alle Indizes erstellen lassen - andernfalls wird er eine böse Überraschung erleben. Sind nämlich die Indizes der aktuellen Datenbank mit einer anderen Sortierfolge als der derzeit aktiven erstellt oder werden mehrere Datenbanken mit unterschiedlichen Sortierfolgen in der Abfrage angesprochen, schaltet sich Rushmore einfach ab. Ein Zugriff auf 1000 Sätze aus einer Million dauert dann statt wenige Sekunden etliche Stunden, da plötzlich sequentiell durch die Datenbank gefahren wird. Also: Keine gemischten Sortiersequenzen verwenden.
Sofern man auf mehreren Betriebssystemen arbeitet oder auf Datenbanken von anderen Plattformen zugreift, benötigt man die automatische Codepage-Konvertierung. DOS-Datenbanken haben die Codepages 437 oder 850, Windows-Datenbanken hingegen die Codepage 1252. Die Konvertierung betrifft Umlaute und Sonderzeichen. In der Praxis tritt die Kombination von Codepages in größeren Netzwerken mit getrennten Erfassungsarbeitsplätzen, meist unter DOS, und Auswertungsplätzen, unter Windows, vor. Da die Codepage-Konvertierung zwar sehr schnell ist, bei großen SQL-Abfragen aber dennoch einige Prozente zur Verarbeitungszeit beiträgt, sollte man die Codepages auf die der Abfrageplätze umstellen. Dies geht allerdings nicht durch Änderung der Codepage-Markierung in der Datenbank sondern erfordert zusätzlich das Konvertieren mit den Funktionen ANSITOOEM() und OEMTOANSI().
Die Rushmore-Optimierung wird durch die Notwendigkeit exakter Vergleiche ebenfalls gebremst. Die möglichen globalen Einstellungen SET EXACT OFF und SET ANSI OFF versprechen bei SQL-Abfragen die höchste Geschwindigkeit. Die im Einzelfall notwendige exakte Übereinstimmung kann man durch ein doppeltes Gleichheitszeichen überprüfen.
Wie bei jeder Medizin gibt es auch hier eine Gegenanzeige. Rushmore verträgt es nicht unbedingt, wenn man bei Ersetzungsvorgängen Felder verändert, die auch in der Filterbedingung verwendet wurden. Bei jeder Ersetzung wird ja auch der Index korrigiert und damit die relative Position des Datensatzes innerhalb des Index verändert. In solchen Fällen sollte man die Optimierung temporär abschalten. Dies geschieht durch den globalen Schalter SET OPTIMIZE OFF oder durch die zusätzliche Klausel NOOPTIMIZE.
Bekannte Probleme bei der Rushmore-Optimierung gibt es im Augenblick nur mit umgedrehten Indizes und bei Unterabfragen in SQL. Bei Abfragen auf Feldern mit einer umgedrehten Sortierung, also dem höchsten statt dem niedrigsten Wert zuerst, schaut Rushmore sozusagen "in die falsche Richtung". Bei der Schachtelung von SQL-Abfragen mit Unterabfragen werden die Unterabfragen zwar optimiert, aber bei jedem Aufruf wieder neu. Das ließe sich theoretisch noch beschleunigen, ist aber im eigentlichen Sinne kein Fehler.