PostgreSQL
 sql >> Datenbank >  >> RDS >> PostgreSQL

Postgresql-Index für xpath-Ausdruck gibt keine Beschleunigung

Nun, zumindest wird der Index verwendet. Sie erhalten jedoch einen Bitmap-Index-Scan anstelle eines normalen Index-Scans, was bedeutet, dass die Funktion xpath() viele Male aufgerufen wird.

Machen wir eine kleine Überprüfung :

CREATE TABLE foo ( id serial primary key, x xml, h hstore );
insert into foo (x,h) select XMLPARSE( CONTENT '<row  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">  
   <object_id>2</object_id>  
   <pack_form_id>' || n || '</pack_form_id>  
   <prod_form_id>34</prod_form_id>
 </row>' ), 
('object_id=>2,prod_form_id=>34,pack_form_id=>'||n)::hstore 
FROM generate_series( 1,100000 ) n;

test=> EXPLAIN ANALYZE SELECT count(*) FROM foo;
                                                   QUERY PLAN                                                    
-----------------------------------------------------------------------------------------------------------------
 Aggregate  (cost=4821.00..4821.01 rows=1 width=0) (actual time=24.694..24.694 rows=1 loops=1)
   ->  Seq Scan on foo  (cost=0.00..4571.00 rows=100000 width=0) (actual time=0.006..13.996 rows=100000 loops=1)
 Total runtime: 24.730 ms

test=> explain analyze select * from foo where (h->'pack_form_id')='123';
                                             QUERY PLAN                                             
----------------------------------------------------------------------------------------------------
 Seq Scan on foo  (cost=0.00..5571.00 rows=500 width=68) (actual time=0.075..48.763 rows=1 loops=1)
   Filter: ((h -> 'pack_form_id'::text) = '123'::text)
 Total runtime: 36.808 ms

test=> explain analyze select * from foo where ((xpath('//pack_form_id/text()'::text, x))[1]::text) = '123';
                                              QUERY PLAN                                              
------------------------------------------------------------------------------------------------------
 Seq Scan on foo  (cost=0.00..5071.00 rows=500 width=68) (actual time=4.271..3368.838 rows=1 loops=1)
   Filter: (((xpath('//pack_form_id/text()'::text, x, '{}'::text[]))[1])::text = '123'::text)
 Total runtime: 3368.865 ms

Wie wir sehen können,

  • das Scannen der gesamten Tabelle mit count(*) dauert 25 ms
  • Das Extrahieren eines Schlüssels/Werts aus einem hstore verursacht kleine zusätzliche Kosten, etwa 0,12 µs/Zeile
  • Das Extrahieren eines Schlüssels/Werts aus einer XML-Datei mit xpath verursacht enorme Kosten, etwa 33 µs/Zeile

Schlussfolgerungen :

  • xml ist langsam (aber das weiß jeder)
  • wenn Sie einen flexiblen Schlüssel/Wert-Speicher in eine Spalte einfügen möchten, verwenden Sie hstore

Da Ihre XML-Daten ziemlich groß sind, werden sie geröstet (komprimiert und außerhalb der Haupttabelle gespeichert). Dadurch werden die Zeilen in der Haupttabelle viel kleiner, daher mehr Zeilen pro Seite, was die Effizienz von Bitmap-Scans verringert, da alle Zeilen auf einer Seite erneut überprüft werden müssen.

Sie können dies jedoch beheben. Aus irgendeinem Grund hat die xpath()-Funktion (die sehr langsam ist, da sie XML verarbeitet) die gleichen Kosten (1 Einheit) wie beispielsweise der Integer-Operator "+"...

update pg_proc set procost=1000 where proname='xpath';

Möglicherweise müssen Sie den Kostenwert anpassen. Wenn er die richtigen Informationen erhält, weiß der Planer, dass xpath langsam ist, und vermeidet einen Bitmap-Index-Scan, indem er stattdessen einen Index-Scan verwendet, bei dem die Bedingung nicht für alle Zeilen auf einer Seite erneut überprüft werden muss.

Beachten Sie, dass dies das Problem der Zeilenschätzungen nicht löst. Da Sie das Innere des xml (oder hstore) nicht ANALYSIEREN können, erhalten Sie Standardschätzungen für die Anzahl der Zeilen (hier 500). Der Planer kann also völlig falsch liegen und einen Katastrophenplan wählen, wenn einige Verbindungen involviert sind. Die einzige Lösung dafür ist die Verwendung geeigneter Spalten.