Posts mit dem Label SYNC werden angezeigt. Alle Posts anzeigen
Posts mit dem Label SYNC werden angezeigt. Alle Posts anzeigen

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.

Donnerstag, 15. Mai 2008

DML Operationen und der Oracle Text Index

Im Folgenden wollen wir die Möglichkeiten der Oracle Text Index-Maintenance in Verbindung mit DML-Operationen demonstrieren. Nehmen wir die Beispieltabelle und den Text Index aus dem ersten Blog als Grundlage. Der Inhalt der Dokumenttabelle sieht folgendermassen aus:


  ID DOKUMENT
---- ---------------------------------------------------------------------
1 A-Partei gewinnt Wahl in Hansestadt
2 Terror in Nahost: Kriminalität steigt immer weiter an
3 Wirtschaft: Erneuter Gewinnzuwachs in diesem Jahr
4 Olympia rückt näher: Der Fackellauf ist in vollem Gange
5 Wer wird US-Präsident? Obama und Clinton machen Wahlkampf
6 Papst bestürzt über jüngsten Skandal!
7 Wahlkampf in den USA geht weiter: Clinton und Obama LIVE zu sehen
8 Software-Kenntnisse werden immer wichtiger
9 Umfrage: Alle wollen mehr Geld!
10 Der Papst liest seine erste Messe in den USA

Werden zusätzliche Dokumente in unsere Dokumenttabelle eingefügt, wird der Index nicht automatisch aktualisiert. Um den Index up-to-date zu haben, muss der Index synchronisiert werden. Dies kann man manuell mit der Prozedur CTX_DDL.SYNC_INDEX erreichen oder aber automatisch in periodischen Abständen in Verbindung mit dem DBMS_JOB oder ab 10g mit dem DBMS_SCHEDULER Paket. In 10g ist es nun zusätzlich möglich diese Operation ganz bequem beim CREATE INDEX oder dem ALTER INDEX REBUILD PARAMETERS mitanzugeben.
In folgendem Beispiel wird der Sync-Scheduler-Job alle 5 Minuten durchgeführt; dabei wird 15MB Memory zur Verfügung gestellt und mit der Parallelisierung von 2 gearbeitet.
SQL> alter index idx_text rebuild parameters ('replace metadata sync (every "SYSDATE+5/1440" parallel 2 memory 15M)');
Nun fügen wir eine weitere Zeile ein und versuchen das Resultat zu selektieren:
SQL>insert into texttabelle values (seq_texttabelle.nextval, 'Muppet-Show der Steuerversprecher');
SQL>commit;
SQL>select * from texttabelle where contains(dokument, 'Muppet')>0;
no rows selected
Monitoring ist möglich über CTX_USER_PENDING View (oder ctxsys.dr$pending View):
SQL> select pnd_index_name,pnd_rowid,pnd_timestamp from ctx_user_pending;
PND_INDEX_NAME  PND_ROWID                  PND_TIMESTAMP
--------------- -------------------------- --------------
IDX_TEXT        AAAUYsAAEAABfg0AAK         16-05-08 09:21
Und nun das Ergebnis nach kurzer Wartezeit:
SQL>select * from texttabelle where contains(dokument, 'Muppet')>0;
  ID DOKUMENT
---- ------------------------------------------------------------
  11 Muppet-Show der Steuerversprecher

Falls die Wartezeit bis zur Synchronisierung zu lange ist, hat man in 10g die Möglichkeit Intervalle zum COMMIT Zeitpunkt oder aber bzgl. der Transaktion zu wählen. Folgendes Beispiel zeigt den Einsatz der COMMIT Option:
SQL>alter index idx_text rebuild parameters ('replace metadata sync (on commit)');

SQL>insert into texttabelle values (seq_texttabelle.nextval, 'Bahn-Aufsichtsrat macht Weg frei für Börsengang');
SQL>select * from texttabelle where contains(dokument, 'Weg')>0;
no rows selected
SQL>commit;
SQL>select * from texttabelle where contains(dokument, 'Weg')>0;
 ID DOKUMENT
--- ------------------------------------------------------------
 12 Bahn- Aufsichtsrat macht Weg frei für Börsengang

Die COMMIT Option erlaubt allerdings keine zusätzlichen Memory- oder PARALLEL- Optionsangaben im Statement. Ausserdem könnte der Index durch häufiges COMMIT auch fragmentiert werden. Die zweite Option ist ein transaktionsbezogenes Intervall (auch transactional query genannt) auszuwählen. Der Index wird auch hier entweder mit CREATE INDEX oder folgendem ALTER INDEX REBUILD Statement erzeugt.
SQL>alter index idx_text rebuild parameters ('replace metadata transactional');

Das folgende Beispiel zeigt das Verhalten:
SQL>select count(*) from texttabelle where contains(dokument, 'im all')>0; -- kein Treffer
SQL>insert into texttabelle values (seq_texttabelle.nextval, 'Europäer im All');
SQL>select * from ctxsys.dr$unindexed;
UNX_IDX_ID UNX_IXP_ID UNX_ROWID
---------- ---------- ------------------
1457       0          AAAUVdAAEAABfdUAAO
SQL>select count(*) from texttabelle where contains(dokument, 'im all')>0; -- 1 Treffer
SQL>rollback;
SQL>select count(*) from texttabelle where contains(dokument, 'im all')>0; -- kein Treffer
Und wie funktioniert das Ganze?
UPDATE und INSERT Statements eines transaktionalen Index werden wie beim normalen Index in der dr$pending View mitgeloggt. Zusätzlich dazu werden die ROWIDs in dr$unindexed mitgeschrieben. Während einer Abfrage wird jede ROWID in dr$unindexed mit den Ergebnissen aus der $I Tabelle evaluiert. Die Menge der ROWIDS von dr$unindexed wird mit dem Resultat der $I Tabelle kombiniert und liefert die Endresultate.
Transactional Queries ersetzen allerdings keinen SYNC-Prozess. Um zu verhindern, dass die Queries immer langsamer werden mit wachsender dr$unindexed Tabelle, ist es notwendig ein sinnvolles Intervall für den SYNC-Prozess einzustellen.
Und am Schluss noch ein kleiner Tipp: Benötigen einige Abfragen sofortige Ergebnisse bei der Suche und andere nicht, kann das Feature einfach ein- und ausgeschaltet werden mit:
SQL>exec ctx_query.disable_transactional_query := TRUE;

Viel Spass beim Ausprobieren ...



Beliebte Postings