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 ...



Kommentare:

Andreas hat gesagt…

Wie schaffe ich es, das die Syncronisation des Text Indexes bei einem Multi_Column_Datastore greift?

Folgendes Szenario: Ich möchte einen Namessuche (first_name, last_name) mit Hilfe von Oracle Text bauen. Es existieren zwei Spalten, die über einen Multi_Column_Datastore in einem Index durchsucht werden sollen.
Ich erstelle den Index mit der "sync (on commit)"-Option und kann dabei nur eine der beiden Spalten an dieser Stelle angeben. Ich habe mich an dieser Stelle fuer den "Last_Name" entschieden.



Wenn ich einen Nachname ändere greift die Syncronisation wunderbar.

Wenn ich allerdings nur den Vornamen ändere, schlägt sich das nicht automatisch bis in den Index durch.
Ich moechte gern die "on commit" Variante nutzen, da nicht allzu viele DML's in der Tabelle zu erwarten sind.

Wie kann ich auch die Aenderungen des Vornames bei der Suche beruecksichten bzw. den Index automatisch aktuell halten?

- Trigger und Scheduler Jobs wollte ich vermeiden.

- die Option sync (every "SYSDATE+5/1440") scheitert an einem DRG-50857: oracle error in drvddl.Resubmit_job
ORA-27486: Nicht ausreichende Berechtigungen . Welche Berechtigungen sind notwendig? Der Datenbankuser besitzt schon die Rolle "CTXAPP".

Lg

Andreas

Ulrike Schwinn hat gesagt…

Hallo Andreas,
Du musst eine Dummy Spalte und zum Beispiel einen Before Update Trigger verwenden, damit der Textindex die Änderungen auf allen Spalten bemerkt. Wir haben dazu vor längerer Zeit einen Blog veröffentlicht. Du findest diese Info unter
http://oracle-text-de.blogspot.com/2008/11/mehrere-tabellenspalten-indizieren.html
Ich hoffe, das hilft Dir weiter. Gruß Ulrike

Anonym hat gesagt…

Hallo liebe Oracle Text Anwender!

Vorsicht ist geboten bei Massenverarbeitung in Zusammenhang mit Oracle-Text.
Denn obwohl man sich mit einem zeitgesteuerten Sync (gefühlter Asyncronität) in Sicherheit wiegt, schlagen DELETES und UPDATES direkt auf den Index durch.
Jeweils mit einem DELETE, ein UPDATE pflegt noch die dr$unindexed mit dem neuen Wert.
Also: Jedes DML außer INSERT impliziert ein
begin ctxsys.syncrn(:idxownid, :idxoname, :idxid, :ixpid, :rtabnm, :flg); end;
und birgt damit Gefahr von Row Lock-Contention auf die $R (DocID/Rowid-)Mappingtabelle.

Hilfe verspricht Patch 14186396, der dafür sorgt, mehr Records in der $R Tabelle vorzufinden.


Viele Grüße,
Mirko Geißler

Anonym hat gesagt…

Ich sehe nach einem Sync alte Sätze in der "Pending" Table. Wieso? Wie wird man diese los

Beliebte Postings