Data Import Handler – import danych z baz SQL (cz. 2)

W pierwszej części udało nam się zindeksować informacje zawarte w bazie danych. W części drugiej spróbujemy rozszerzyć funkcjonalność importu o import przyrostowy.

Dokumentów było trochę powyżej 1mln i zajęło to niecałe pół godziny. W zasadzie tu moglibyśmy zakończyć kwestię importu, ale wyobraźmy sobie, że chcielibyśmy te dane aktualizować w indeksie na bieżąco, w miarę, jak zmieniają się one w źródle. Nie będzie to oczywiście prawdziwy RTS (real time search) – pomiędzy zmianą danych a ich udostępnieniem w systemie wyszukiwawczym minie trochę czasu, ale załóżmy, że aktualizacja co np. godzinę jest wystarczająca. Pierwszą rzeczą, którą musimy wykonać w celu implementacji indeksowania przyrostowego jest przygotowanie bazy danych.

Przygotowanie bazy danych

Indeksowanie przyrostowe wymaga możliwości uzyskania z bazy danych informacji: Jakie dokumenty zmieniły się od ostatniej indeksacji. Jeśli mamy szczęście, to dane takie są dostępne – jeśli pecha: musimy zmodyfikować istniejącą strukturę bazy danych. W zależności od bazy danych, możliwości zmiany zastanego schematu bazy i aplikacji korzystających z bazy, mamy kilka możliwości. My w swojej praktyce wykorzystywaliśmy najczęściej:

  • dodanie dodatkowej kolumny z dokładną datą ostatniej modyfikacji, uaktualnianej automatycznie (np. trigger lub default/on update w mysql) lub (gorsze rozwiązanie) „ręcznie” (przez aplikacje)
  • stworzenie kolejki zleceń – wpisywanie (np. triggerem ) identyfikatorów zmienionych dokumentów w oddzielnej tabeli

Oba rozwiązania wymagają zwrócenia uwagę na zmiany danych wszystkich encji wchodzących w skład dokumentu.

Wracając do naszego przykładu z części pierwszej (polska wikipedia, zaimportowana do bazy postgreSQL, do tabel aplikacji mediawiki), nasza struktura wygląda tak:

Tabela „page”:

ColumnTypeModifiers
page_idintegernot null default nextval(‘page_page_id_seq’::regclass)
page_titletextnot null
page_restrictionstext
page_counterbigintnot null default 0
page_is_redirectsmallintnot null default 0
page_is_newsmallintnot null default 0
page_randomnumeric(15,14)not null default random()
page_touched timestamp with time zone
page_latest integernot null
page_lenintegernot null
titlevectortsvector

Tabela: „revision”:

ColumnTypeModifier
rev_idintegernot null default nextval('revision_rev_id_seq'::regclass)
rev_pageinteger
rev_text_idinteger
rev_commenttext
rev_userintegernot null
rev_user_texttextnot null
rev_timestamptimestamp with time zonenot null
rev_minor_editsmallintnot null default 0
rev_deletedsmallintnot null default 0
rev_leninteger
rev_parent_idinteger

Tabela: „pagecontent”

ColumnTypeModifier
old_idintegernot null default nextval('text_old_id_seq'::regclass)
old_texttext
old_flagstext
textvectortsvector

Pierwsza tabela zawiera kolumnę „page_touched”, druga: „rev_timestamp” co wygląda na dokładnie to co potrzebujemy: datę modyfikacji. Trzecia tabela nie ma takiego pola, ale zawiera teksty dla konkretnej wersji strony – teksty te nie zmieniają się w czasie – gdy użytkownik modyfikuje stronę, dochodzi tylko nowa wersja.

Przypomnijmy sobie definicję źródła z pierwszej części:

To co musimy zrobić to dodać definicje zapytań wykorzystywanych w indeksowaniu przyrostowych. Nic prostszego:

No dobrze – są prostsze rzeczy 🙂

Porównując oba pliki dostrzegamy tylko dodatkowe definicje dwóch atrybutów:

  • deltaQuery – zapytanie odpowiedzialne za zwrócenie identyfikatorów tych rekordów, które zmieniły się od ostatniego indeksowania (pełnego lub przyrostowego) – czas ostatniego indeksowania DIH dostarcza w zmiennej:${dataimporter.last_index_time}. To zapytanie jest używane przez SOLR do znajdowania tych rekordów, które się zmieniły.
  • deltaImportQuery – zapytanie zwracające dane dla rekordu o identyfikatorze podanym jako zmienna DIH: ${dataimport.delta.id}.
  • parentDeltaQuery – zapytanie zwracające dane dla rekordu encji-rodzica. Dzięki tym zapytaniom SOLR jest w stanie pobrać wszystkie dane składające się na dokument, niezależnie od tego, z której encji pochodzą. Konieczne jest to dlatego, że silnik indeksowania nie ma możliwości modyfikacji zindeksowanych danych – musimy więc zindeksować cały dokument, niezależnie od tego, że część danych się nie zmieniła.

W naszym przykładzie nie znajdziemy usuwania  dokumentów. Dlatego też odpadł nam problem uwzględniania usuniętych rekordów i usuwania odpowiadających im dokumentów z indeksu. W przypadku, gdy taka funkcjonalność okazała się konieczna możemy posłużyć się wyżej opisanym sposobem z kolejką zleceń, wpisując do niej dokumenty do usunięcia. W konfiguracji DIH skorzystalibyśmy wtedy z atrybutu:

  • deletedPkQuery – dostarcza identyfikatorów usuniętych elementów.

W kolejnym odcinku postaramy się też uporządkować kwestie współpracy z bazą danych, spróbujemy też zrobić jeszcze raz naszą integrację z bazą w trochę inny sposób.

7 thoughts on “Data Import Handler – import danych z baz SQL (cz. 2)

  • 20 grudnia 2010 at 16:03
    Permalink

    Super material. Bardzo przydatny. Mialabym krotkie pytanie. A jak mozna byloby majac np. jedna tabele w bazie danych ktora ma pare milionow rekordow podzielic ja na kilka shards przy indexowaniu rozproszonym wykorzystujac DIH ? Najlepiej wykorzystujac uniqueId.hashCode%numServers, ale nie wiem jak to zdefiniowac w DIH.

    Z gory bardzo dziekuje za jakas wskazowke 🙂

    Reply
  • 20 grudnia 2010 at 16:56
    Permalink

    Dzięki za dobre słowo 🙂

    Nie jestem pewien, czy dobrze zrozumiałem, w czym problem, bo w pytaniu jest w zasadzie podana odpowiedź. Można na każdej instancji zdefiniować zapytanie korzystając z modulo, np:

    SELECT * from components where id % 4 = 3;

    Z ciekawości – ile masz rekordów i jaki wolumen danych?

    Reply
  • 21 grudnia 2010 at 09:44
    Permalink

    1 tabela, 45mln rekordow kolo 13 kolumn w tej tabeli, indexuje tylko kolo 4 kolumn z tej tabeli.

    Ale problem mam wlasnie z shards’ami, bo nigdy tego wczesniej nie robilam. Bede probowala wykorzystac wlasnie ten tutorial na SolrCloud wiki dla Solr+ZooKeeper.

    W tescie z plikami xmlowymi wypadlo ok, dokladnie jak w tutorialu, ale dla DIH nawet 1 tabeli indexowanej w shardach moze byc problem.

    Szkoda, ze nie ma wlasnie jakiegos tutorialu pokazujacego wykorzystanie ZooKeeper+Solr dla DIH.

    Podobnie Zoie wydaje sie warte uwagi, ale jak probowalam uruchomic solr+zoie dla DIH, to padlam juz na wstepnej konfiguracji po otrzmaniu kilku bledow w konsoli 😛 A wszystko wlasnie przez brak przykladow w necie 😉

    Reply
  • 21 grudnia 2010 at 19:17
    Permalink

    jak znajdę chwilę czasu to postaram się sprawdzić, bo chyba nie powinno być problemu. Powiedz jeszcze, której wersji solr używasz?

    Reply
  • 22 grudnia 2010 at 10:11
    Permalink

    Solr 1.4.1 uzywam

    mam pytanie jeszcze odnosnie deletePkQuery
    jak robie update na rekordach to przeszukiwanie uwzglednia zmiany (deltaQuery i deltaImportQuery wydaja sie byc poprawne), ale jak usune jakis rekord w tabeli to dalej mi go wyszukuje jakby dalej byl w bazie.
    Nie znalazlam zadnego przykladu sensownego dla deletePkQuery.

    Z gory dzieki za jakas wskazowke

    Reply
  • 22 grudnia 2010 at 10:15
    Permalink

    Generalnie docelowo interesuje mnie wlasnie kombinacja:
    SolrCloud(Solr1.4.1+ZooKeeper)+Zoie+Bobo

    Nie weim na ile realne sie to okaze w praktyce 😛

    Reply
  • 22 grudnia 2010 at 21:23
    Permalink

    Powiem tak: ambitne plany 🙂 Pamiętaj, że SolrCloud jest w trakcie developmentu – pewne rzeczy nie działają, inne się gryzą. Póki co wymaga to pogrzebania w kodzie, żeby to zmusić jakoś do działania w nietrywialnych projektach.

    Co do shardów + DIH, postaram się w poniedziałek wrzucić jakiś prosty przykład.

    Reply

Dodaj komentarz

Twój adres email nie zostanie opublikowany. Pola, których wypełnienie jest wymagane, są oznaczone symbolem *