Dienstag, 16. September 2008

Index-Synchonisierung und TRANSACTIONAL-Parameter

Eine sehr wichtige Eigenschaft jedes Textindex ist das TRANSACTIONAL Keyword, welches seit Oracle10g verwendet werden kann.
Normalerweise ist es ja so, dass Änderungen an der Dokumenttabelle im Index nicht sofort sichtbar werden, sondern erst nach dem Index Sync. Das kann man sehr schön in der View CTX_USER_PENDING nachvollziehen.
SQL> select PND_INDEX_NAME, PND_ROWID, PND_TIMESTAMP  from ctx_user_pending;

PND_INDEX_NAME                 PND_ROWID          PND_TIMESTAMP
------------------------------ ------------------ -------------------
IDX_DOKUMENT_VOLLTEXT          AAAiPsAAEAAAjUlAAD 16.09.2008 10:18:35
IDX_VOLLTEXT_2                 AAAiPsAAEAAAjUkAAA 29.07.2008 11:12:59
IDX_VOLLTEXT_2                 AAAiPsAAEAAAjUlAAD 16.09.2008 10:18:36
IDX_VOLLTEXT_2                 AAAiPsAAEAAAjUmAAD 29.07.2008 10:50:37
IDX_VOLLTEXT_2                 AAAiPsAAEAAAjUmAAE 29.07.2008 10:59:23
In einer Volltextrecherche sind diese Dokumente normalerweise nicht sichtbar; sie sind noch nicht in den Index synchronisiert. Doch was tut man, wenn die Anforderung besteht, dass alle Dokumente sofort durchsuchbar sein müssen ...?
Man könnte den Index nach jedem COMMIT synchronisieren; damit wäre die Anforderung erstmal erfüllt ... und es gibt sogar einen Parameter dafür: Beim CREATE INDEX kann als Parameter SYNC ON COMMIT mitgegeben werden. Das hat aber einen gewichtigen Nachteil:
Beim Synchronisieren (CTX_DDL.SYNC_INDEX) wird ebenfalls Wert auf möglichst kurze Laufzeit gelegt. Die neuen Informationen (die Tokens des neuen Dokumentes) werden also nicht an die Stellen in den Index eingepflegt, wo es für die Abfrageperformance optimal wäre, sondern dort, wo es am schnellsten geht: quasi "ans Ende" des Index. Die hinsichtlich Abfrageperformance "optimale" Struktur wird erst durch das Optimieren (CTX_DDL.OPTIMIZE_INDEX) erzeugt.
Eine Synchronisierung nach jedem Commit bedeutet also, dass der Index mit jedem Commit weiter "fragmentiert" - die Abfrageperformance also recht schnell immer schlechter wird. Als Faustregel kann man festhalten, dass eine Synchronisation mit so vielen Dokumenten wie möglich stattfinden sollte - auf jeden Fall aber mit mehr als einem.
Das sieht nach einem Dilemma aus: Wenn nun (siehe oben) die Anforderung besteht, dass ein neues Dokument sofort durchsuchbar sein soll, müssten wir ja nach jedem COMMIT synchronisieren - das wollen wir aber nicht, weil der Index dann zu schnell fragmentiert. Und genau hier greift der Parameter TRANSACTIONAL des Volltextindex. Ein transaktionaler Index wird wie folgt erzeugt:
create index idx_volltext on dokumente_tabelle (spalte)
indextype is CTXSYS.CONTEXT
parameters ('TRANSACTIONAL')
Bei Volltextabfragen mit der CONTAINS-Funktion auf diesen Index werden nun auch die noch gar nicht im Index befindlichen Dokumente gefunden. Wird der Parameter gesetzt, so durchsucht Oracle die noch nicht synchronisierten Dokumente (CTX_USER_PENDING) zur Abfragezeit on-the-fly.
Der Vorteil ist nun, dass man nicht mehr "den Druck hat", den Index sofort nach Einfügen eines Dokumentes zu synchronisieren - denn es ist ja auffindbar. Man kann sich nun ein geeignetes Synchronisierungsintervall überlegen, welches die Indexfragmentierung einerseits in Grenzen hält und andererseits häufig genug synchronisiert, so dass nicht zuviele Dokumente on-the-fly durchsucht werden müssen. Denn eins ist auch klar: Die Suche im Index ist auf jeden Fall günstiger als die on-the-fly Suche durch die noch nicht synchronisierten Dokumente.
Generell kann man sagen, dass der Parameter TRANSACTIONAL ab Oracle10g eine gute Sache ist - man kann ihn eigentlich generell setzen. Das Finden eines guten Intervalls zum Synchronisieren und zum Optimieren (also das Finden einer guten Strategie für die Indexwartung) bleibt jedoch weiterhin eine wichtige Aufgabe in einem Oracle TEXT Projekt.

Kommentare:

radfahrer hat gesagt…

Wir haben eine Datenbank mit Kundenadressen und ein paar weiteren Daten in 3 Tabellen mit jeweils 3-8 Mio Rows.

Für unsere interaktive Anwendung zur Pflege und Suche in diesen Tabellen funktioniert ein Oracle Text Index mit User Data Store schon ganz hervorragend.

Leider haben wir zusätzlich die Anforderung, über Batch-Schnittstellen nachts Daten mit anderen Systemen abgleichen zu müssen. Die diversen Batches sind so angelegt, dass sie immer erst am Ende einer Gruppe von Datensätzen committen, damit notfalls ein einfaches Rollback und ein leichter Neustart möglich sind.

Während der Verarbeitung müssen Änderungen jedoch schon zur Suche - zum Beispiel nach Doubletten - verfügbar sein.

So eine Gruppe von datensätzen für einen Batchlauf enthält derzeit bis zu 3000 Datensätze. Bei der Verarbeitung fällt auf, dass die Oracle Text Suche mit Antwortzeiten von ca. 1s beginnt und dann nach einigen hundert Sätzen beginnt dramatisch langsamer zu werden, bis nach der ersten Stunde schon etwa 10s pro Suchergebnis gebraucht werden; wegen der Gesamtmenge der Verarbeitungen ist damit der Batchabgleich über Nacht nicht mehr möglich.

Meine Idee ist derzeit, die aktuellen Änderungen in neu anzulegende temporäre Tabellen mit autonomen Transaktionen zu schreiben und dort temporär einen zweiten Oracle Text Index zu pflegen, der problemlos und schnell zu synchronisieren und vielleicht sogar zu optimieren wäre, da er nur wenige Datensätze enthält.

Die unscharfe Suche würde dann auf den unveränderten Originaltabellen und auf dem temporären Tabellen parallel stattfinden. Am Ende, nach dem alle Batches gelaufen und Committed sind, würde einfach der Gesamtindex neu erstellt werden (das dauert alles zusammen etwa eine halbe Stunde) und die temporären Tabellen könnten wieder geleert werden.

Ist das eine vernünftige Herangehensweise?


Wolfram

Carsten Czarski hat gesagt…

Hallo Wolfram,

3000 Datensätze sind für den TRANSACTIONAL Parameter viel zu viel.

Prinzipiell muss ein COMMIT erfolgen, damit Oracle TEXT die Änderung überhaupt verarbeiten kann - nur dann kommt der Satz in die PENDING Tabelle und steht für einen Sync zur Verfügung. Das Sync muss bei solchen Batch-Operationen schlicht öfter erfolgen. Es sollten eigentlich immer nur kleine Datenmengen in der PENDING stehen, die dann per Sync verarbeitet werden.

Der Weg mit den temporären Tabellen ist gangbar - ab Oracle12c könnte der neue "Near Realtime Index" weiterhelfen ...

Beste Grüße

Carsten

Beliebte Postings