sql >> Database teknologi >  >> RDS >> PostgreSQL

Postgresql-indeks på xpath-udtryk giver ingen hastighed

Nå, i det mindste bruges indekset. Du får dog en bitmap-indeksscanning i stedet for en normal indeksscanning, hvilket betyder, at xpath()-funktionen vil blive kaldt mange gange.

Lad os tage et lille tjek :

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

Som vi kan se,

  • at scanne hele tabellen med count(*) tager 25 ms
  • at udtrække én nøgle/værdi fra en hstore tilføjer en lille ekstra omkostning, ca. 0,12 µs/række
  • at udtrække én nøgle/værdi fra en xml ved hjælp af xpath tilføjer en enorm omkostning, omkring 33 µs/række

Konklusioner:

  • xml er langsom (men det ved alle)
  • hvis du vil placere et fleksibelt nøgle-/værdilager i en kolonne, skal du bruge hstore

Også da dine xml-data er ret store, vil de blive ristet (komprimeret og gemt ude af hovedtabellen). Dette gør rækkerne i hovedtabellen meget mindre, og dermed flere rækker pr. side, hvilket reducerer effektiviteten af ​​bitmap-scanninger, da alle rækker på en side skal kontrolleres igen.

Du kan dog ordne dette. Af en eller anden grund har xpath()-funktionen (som er meget langsom, da den håndterer xml) den samme pris (1 enhed) som f.eks. heltalsoperatoren "+"...

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

Du skal muligvis justere omkostningsværdien. Når de får de rigtige oplysninger, ved planlæggeren, at xpath er langsom og vil undgå en bitmapindeksscanning ved at bruge en indeksscanning i stedet, som ikke behøver at gentjekke betingelsen for alle rækker på en side.

Bemærk, at dette ikke løser problemet med rækkeestimater. Da du ikke kan ANALYSE indersiden af ​​xml (eller hstore), får du standard estimater for antallet af rækker (her 500). Så planlæggeren kan tage helt fejl og vælge en katastrofal plan, hvis nogle joinforbindelser er involveret. Den eneste løsning på dette er at bruge de rigtige kolonner.




  1. Old Style Oracle Outer Join-syntaks - Hvorfor skal du finde (+) på højre side af lighedstegnet i en Left Outer join?

  2. MySQL-valganmodning parallelt (python)

  3. Sådan skriver du en SQL-forespørgsel med dynamisk LIMIT

  4. Konverter 'tid' til 'datetimeoffset' i SQL Server (T-SQL-eksempler)