Dienstag Mai 30, 2006

POSTGRESQL: Migration Oracle / Postgres

Oracle und Postgres sind sich in so mancher Hinsicht recht ähnlich. Darum sieht man jetzt auch viele Projekte, in denen von Oracle zu Postgres migriert wird. Für viele Anwendungen ist Postgres mehr als ausreichend. Nun... Entsprechend vorbereitet, klappt das auch recht gut, wenn einem nicht ein bestimmtes Datentypen/Index-Problem um die Ohren fliegt und das beim Lasttest dann auch nicht auffällt, weil man genau die URL zufälligerweise nicht getestet hat ;-)

Das sieht dann z.B. so aus:

test=> EXPLAIN SELECT * FROM tab1 JOIN tab2 ON tab2.rel2objects=tab1.relation_id WHERE tab2.oid=7929;
                                               QUERY PLAN
Hash Join  (cost=4.11..42522.51 rows=64 width=573)
  Hash Cond: (("outer".relation_id)::text = ("inner".object_assign)::text)
  ->  Seq Scan on tab3  (cost=0.00..34034.29 rows=1131129 width=77)
  ->  Hash  (cost=4.11..4.11 rows=1 width=496)
        ->  Index Scan using idx_tab2 on tab2  (cost=0.00..4.11 rows=1 width=496)
              Index Cond: (oid = 7929)
(6 rows)

Der Hash Join (was mehr oder weniger ein Full Table Scan ist) zeigt es schon: Die Datenbank erwartet Kosten zwischen 4.11 und 42522.51 und beim Seq. Scan sehen wir das über 1,1 Mio. Datensätze durchgegrast werden. Für eine Webapplikation nicht so ganz gut. Und warum ist das so? Wir haben doch entsprechende Indizies, aber die DB nimmt sie nicht her! Hmmm... Bei Oracle war der Execution Plan einwandfrei. Bauen wir die Query mal wie folgt um:

test=> EXPLAIN SELECT * FROM tab1 JOIN tab2 ON tab2.rel2objects::integer=rel_o.relation_id WHERE tab2.oid=7929;
                                            QUERY PLAN
Nested Loop  (cost=0.00..13.53 rows=18 width=573)
  ->  Index Scan using idx_tab2 on tab2  (cost=0.00..4.11 rows=1 width=496)
        Index Cond: (oid = 7929)
  ->  Index Scan using idx_tab1 on tab1  (cost=0.00..9.15 rows=18 width=77)
        Index Cond: (("outer".object_assign)::integer = tab3.relation_id)
(5 rows)

Alles was ich geändert habe, ist der Integer-Cast (... tab2.rel2objects::integer=...). Der Witz an der Sache ist, das rel2objects vom Datentyp Varchar ist und relation_id ist ein Integer. Damit werden für Postgres die Indizies wertlos. Ein expliziter Cast tut Not. Und wie man sieht, ist der Ausführungsplan nun schon erheblich besser. Oracle hat die gleiche Query hingegen ganz anders behandelt. Das Casten war hier kein Thema. Die Oracle bekommt allerdings die totale Krise, wenn die Optimizer Statistiken nicht stimmen. Das stört die Postgres nicht so stark. Allerdings wird diese wiederum sauer, wenn die Datentypen beim Join nicht exakt stimmen. Auch int, int4 und int8 sind für die Postgres nicht gleich und würde auch hier eventl. vorhandene Indizies nicht verwenden. Es hilft also nur, den Ausführungsplan zu checken, um sich vor bösen Überraschungen bei der Migration einigermaßen zu schützen.

Kommentare:

Senden Sie einen Kommentar:
  • HTML Syntax: Ausgeschaltet