sql >> Database >  >> RDS >> PostgreSQL

Postgresql-index op xpath-expressie geeft geen versnelling

Nou, in ieder geval wordt de index gebruikt. Je krijgt echter een bitmap-indexscan in plaats van een normale indexscan, wat betekent dat de xpath()-functie heel vaak wordt aangeroepen.

Laten we een kleine controle doen:

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

Zoals we kunnen zien,

  • het scannen van de hele tabel met count(*) duurt 25 ms
  • het extraheren van één sleutel/waarde uit een hstore brengt een kleine meerprijs met zich mee, ongeveer 0,12 µs/rij
  • het extraheren van één sleutel/waarde uit een xml met xpath brengt enorme kosten met zich mee, ongeveer 33 µs/rij

Conclusies :

  • xml is traag (maar dat weet iedereen)
  • als u een flexibele sleutel/waarde-opslag in een kolom wilt plaatsen, gebruikt u hstore

Omdat je xml-gegevens behoorlijk groot zijn, worden ze ook geroosterd (gecomprimeerd en opgeslagen buiten de hoofdtabel). Dit maakt de rijen in de hoofdtabel veel kleiner, dus meer rijen per pagina, wat de efficiëntie van bitmapscans vermindert omdat alle rijen op een pagina opnieuw moeten worden gecontroleerd.

Je kunt dit wel oplossen. Om de een of andere reden heeft de xpath()-functie (die erg traag is, aangezien het XML verwerkt) dezelfde kosten (1 eenheid) als bijvoorbeeld de integer-operator "+"...

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

Mogelijk moet u de kostprijs aanpassen. Wanneer de planner de juiste informatie krijgt, weet de planner dat xpath traag is en zal hij een bitmapindexscan vermijden, maar in plaats daarvan een indexscan gebruiken, waarbij de voorwaarde voor alle rijen op een pagina niet opnieuw hoeft te worden gecontroleerd.

Merk op dat dit het probleem van rijschattingen niet oplost. Aangezien je de binnenkant van de xml (of hstore) niet kunt ANALYSEREN, krijg je standaardschattingen voor het aantal rijen (hier 500). Dus de planner kan het helemaal bij het verkeerde eind hebben en een rampzalig plan kiezen als er enkele joins bij betrokken zijn. De enige oplossing hiervoor is het gebruik van de juiste kolommen.




  1. Fix "Kan niet installeren in Homebrew op ARM-processor in Intel standaard prefix (/usr/local)!"

  2. Update met functie die eenmaal wordt aangeroepen voor elke rij in Postgres 8.4

  3. kan geen extensie maken zonder de rol van supergebruiker

  4. Sla grote JSON-bestanden op in Oracle DB