Montag, 2. November 2009

Hochverfügbarer Textindex mit Schattenindex-Technologie

Heutzutage müssen auch Anwendungen mit Oracle Textindizes hochverfügbar sein. Allerdings kann schon ein REBUILD des Index dazu führen, dass der Index nicht mehr online zur Verfügung steht. Ein Neuaufbau des Index könnte z.B. aus mehreren Gründen nötig werden:
  • Spezielle Index-Preferences bzw. -Optionen wie Lexereinstellungen sollen geändert werden oder Stoppwörter hinzugefügt werden.
  • Aus Maintenance- und Performance-Gründen soll der Index neu aufgebaut werden - um z.B. eine stark vergrößerte $I Tabelle zu optimieren.
Ein REBUILD des Index ist bis einschliesslich Oracle Database 10g nicht immer vollständig online möglich. Eine mögliche Lösung wäre einen zusätzlichen zweiten Schatten-Index manuell zu erzeugen und diesen mitzupflegen. Die Tabelle könnte dabei folgendermassen aussehen:

CREATE TABLE mytable (text CLOB, dummy1 CHAR, dummy2 CHAR);

Da nur ein einziger Textindex auf einer Spalte möglich ist, wird die Indizierung über den Einsatz von USER_DATA_STORE bzw. MULTI_COLUMN_DATATORE gelöst. Diese erlauben es, einen Index auf der Spalte DUMMY1 zu erzeugen, aber die aktuellen zu indizierenden Daten werden von einer anderen Spalte z.B. TEXT geliefert. Die Abfragen sehen also folgendermassen aus:

... WHERE CONTAINS (dummy1, '') > 0

Im Falle eines Neuaufbaus steht die Spalte DUMMY2 zur Verfügung, die ihre Daten ebenfalls aus der Spalte TEXT erhält. Falls der Aufbau beendet ist, müssen die Queries folgendermassen geändert werden:

... WHERE CONTAINS (dummy2, '') > 0

In Oracle Database 11g ist es nun möglich, ein vollständiges REBUILD online durchzuführen - entweder in einem Ein-Schritt-Verfahren oder um mehr Kontrolle zu gewährleisten bzw. falls Partitionen verwendet werden als mehrstufiger Prozess. Die Idee dabei ist der Einsatz eines Schatten-Index (auch Shadow Index), der parallel zum urspünglichen Indexaufbau mitgeführt wird. Um die Nutzung an einem Beispiel zu demonstrieren, nehmen wir folgenden Index, der auf eine Tabelle SEC_TABLE und der Spalte TEXT erzeugt wurde als Grundlage.

CREATE INDEX sec_ind ON sec_table(text) INDEXTYPE IS ctxsys.context PARALLEL 4;
SELECT * FROM ctx_user_index_errors;
no rows selected

Nun wollen wir eine zusätzliche Spalte für Sprachen ergänzen und den folgenden Multi-Lexer nutzen.

execute ctx_ddl.create_preference('en_lx','basic_lexer');
execute ctx_ddl.set_attribute('en_lx','index_themes','yes');
execute ctx_ddl.create_preference('f_lx','basic_lexer');
execute ctx_ddl.set_attribute('f_lx','base_letter','yes');
execute ctx_ddl.create_preference('d_lx','basic_lexer');
execute ctx_ddl.set_attribute('d_lx','composite','german');
execute ctx_ddl.set_attribute('d_lx','mixed_case','yes');
execute ctx_ddl.set_attribute('d_lx','alternate_spelling','german');
begin
ctx_ddl.create_preference('multi_lx','multi_lexer');
ctx_ddl.add_sub_lexer('multi_lx','german','d_lx');
ctx_ddl.add_sub_lexer('multi_lx','french','f_lx');
ctx_ddl.add_sub_lexer('multi_lx','default','en_lx');
end;
PL/SQL procedure successfully completed.

ALTER TABLE sec_table ADD (lang varchar2(10) default 'de');

Nun erzeugen wir den Schattenindex in einem Einschrittverfahren. Laut Syntax ist zwar die Angabe einer DOP (Degree Of Parallelism) möglich, leider aber noch nicht im aktuellen Release verwendbar. Die Dokumentation gibt dazu folgenden Hinweis: "Reserved for future use. Specify the degree of parallelism. Parallel operation is not supported in the current release."

SQL> execute CTX_DDL.RECREATE_INDEX_ONLINE('SEC_IND','REPLACE LEXER multi_lx language column lang');
PL/SQL procedure successfully completed.

Schaut man zwischendurch in CTX_USER_INDEXES kann man den Aufbau des temporären Index (siehe Präfix RIO) monitoren.

SQL> SELECT idx_name, idx_table, idx_status FROM ctx_user_indexes;
IDX_NAME                       IDX_TABLE                      IDX_STATUS
------------------------------ ------------------------------ ------------
BASIC_IND1                     BASIC_TABLE1                   INDEXED
COMPRESS_IND                   COMPRESS_TABLE                 INDEXED
RIO$1880                       SEC_TABLE                      POPULATE
SEC_IND                        SEC_TABLE                      INDEXED

Nach Aufbau der Schattenindexstruktur erfolgt automatisch ein Austausch (auch Exchange). Textabfragen sind zu jedem Zeitpunkt möglich und dabei ist kein Eingriff in die Applikationen notwendig. Überprüft man zum Schluss die Struktur, kann man die Veränderungen beispielsweise mit CTX_REPORT.CREATE_INDEX_SCRIPT feststellen.

SQL> set long 10000
SQL> set pagesize 1000
SQL> SELECT ctx_report.create_index_script('SEC_IND') FROM dual;

CTX_REPORT.CREATE_INDEX_SCRIPT('SEC_IND')
--------------------------------------------------------------------------------
begin
  ctx_ddl.create_preference('"SEC_IND_DST"','DIRECT_DATASTORE');
end;
/
begin
  ctx_ddl.create_preference('"SEC_IND_FIL"','NULL_FILTER');
end;
/
begin
  ctx_ddl.create_section_group('"SEC_IND_SGP"','NULL_SECTION_GROUP');
end;
/
begin
  ctx_ddl.create_preference('"SEC_IND_LEX"','MULTI_LEXER');
end;
/
begin
  ctx_ddl.create_preference('"SEC_IND_L00"','BASIC_LEXER');
  ctx_ddl.set_attribute('"SEC_IND_L00"','INDEX_THEMES','YES');
end;
/
begin
  ctx_ddl.create_preference('"SEC_IND_LF"','BASIC_LEXER');
  ctx_ddl.set_attribute('"SEC_IND_LF"','BASE_LETTER','YES');
end;
/
begin
  ctx_ddl.create_preference('"SEC_IND_LD"','BASIC_LEXER');
  ctx_ddl.set_attribute('"SEC_IND_LD"','COMPOSITE','GERMAN');
  ctx_ddl.set_attribute('"SEC_IND_LD"','MIXED_CASE','YES');
  ctx_ddl.set_attribute('"SEC_IND_LD"','ALTERNATE_SPELLING','GERMAN');
end;
/
begin
  ctx_ddl.add_sub_lexer('"SEC_IND_LEX"','DEFAULT','"SEC_IND_L00"');
  ctx_ddl.add_sub_lexer('"SEC_IND_LEX"','FRENCH','"SEC_IND_LF"');
  ctx_ddl.add_sub_lexer('"SEC_IND_LEX"','GERMAN','"SEC_IND_LD"');
end;
/
begin
  ctx_ddl.create_preference('"SEC_IND_WDL"','BASIC_WORDLIST');
  ctx_ddl.set_attribute('"SEC_IND_WDL"','STEMMER','ENGLISH');
  ctx_ddl.set_attribute('"SEC_IND_WDL"','FUZZY_MATCH','GENERIC');
end;
/
begin
  ctx_ddl.create_stoplist('"SEC_IND_SPL"','BASIC_STOPLIST');
...

Möchte man das Ganze kontrolliert in einem 2 Schrittverfahren durchführen, kann man die folgenden beiden Prozeduren verwenden. Auch hier ist noch (im aktuellen Release) keine Parallelisierung möglich.

execute CTX_DDL.CREATE_SHADOW_INDEX (idx_name=>'SEC_IND',parameter_string=>'REPLACE LEXER multi_lx language column lang');
execute CTX_DDL.EXCHANGE_SHADOW_INDEX(idx_name=>'SEC_IND');

Um den Schattenindex zu löschen, kann man die spezielle Prozedur CTX_DDL.DROP_SHADOW_INDEX verwenden. Mehr Tipps und Tricks in einem der nächsten Blogs.....

Kommentare:

m.szabo hat gesagt…

es ist möglich den recreate noch etwas zu tunen:
exec CTX_DDL.RECREATE_INDEX_ONLINE('IDX_ABC', 'replace memory 2047M', 1, NULL);

parallel degree soll wahrscheinlich in 12g kommen.


gruß
m. szabo

Mirko hat gesagt…



Noch ein kleiner Tipp/Beobachtung bei zeitgesteuertem Textindex-Sync:
Während der Neuanlage im "Schatten" (also recreate_index_online oder exchange_shadow_index) werden die parallel dazu durchgeführten Änderungen nur am Original-Index gepflegt und fehlen letzendlich in der fertiggestellten Kopie.
Ich empfehle den Sync-Job in der Zeit des Neuaufbaus zu deaktivieren, um ihn am Ende gleich die neuen Strukturen pflegen zu lassen...

begin
sys.dbms_scheduler.disable(name => 'DR$$J');

/* .... ctx_ddl.recreate_index_online ... */

sys.dbms_scheduler.enable(name => 'DR$$J');
end;
/


Viele Grüße,
Mirko Geißler

Ulrike Schwinn hat gesagt…

Das ist nicht ganz korrekt oder etwas irreführend. Die Änderungen landen in der Pending Queue (dr$pending_online, dr$pending etc.) und am Schluß wird alles nachsynchronisiert, somit wird am Schluß nichts fehlen.

Beliebte Postings