sql >> Databáze >  >> RDS >> PostgreSQL

Postgresql index ve výrazu xpath nezrychluje

No, alespoň se používá index. Získáte však skenování indexu bitmapy namísto normálního skenování indexu, což znamená, že funkce xpath() bude volána mnohokrát.

Udělejme malou kontrolu :

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

Jak vidíme,

  • skenování celé tabulky pomocí count(*) trvá 25 ms
  • Extrahování jednoho klíče/hodnoty z hstore znamená malé dodatečné náklady, asi 0,12 µs/řádek
  • Extrakce jednoho klíče/hodnoty z xml pomocí xpath zvyšuje náklady, asi 33 µs/řádek

Závěry:

  • xml je pomalý (ale každý to ví)
  • Pokud chcete do sloupce umístit flexibilní úložiště párů klíč/hodnota, použijte hstore

Vzhledem k tomu, že vaše xml data jsou poměrně velká, budou také toastována (komprimována a uložena mimo hlavní stůl). Díky tomu jsou řádky v hlavní tabulce mnohem menší, a tudíž více řádků na stránku, což snižuje efektivitu skenování bitmapy, protože je nutné znovu zkontrolovat všechny řádky na stránce.

Můžete to však opravit. Z nějakého důvodu má funkce xpath() (která je velmi pomalá, protože zpracovává xml) stejnou cenu (1 jednotka) jako řekněme celočíselný operátor "+"...

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

Možná budete muset upravit hodnotu nákladů. Když jsou mu poskytnuty správné informace, plánovač ví, že xpath je pomalý a vyhne se skenování indexu bitmapy, místo toho použije skenování indexu, které nepotřebuje znovu kontrolovat podmínku pro všechny řádky na stránce.

Všimněte si, že to nevyřeší problém s odhady řádků. Protože nemůžete analyzovat vnitřek xml (nebo hstore), získáte výchozí odhady pro počet řádků (zde 500). Takže plánovač se může úplně mýlit a zvolit katastrofický plán, pokud se jedná o nějaké spoje. Jediným řešením je použití správných sloupců.




  1. životopis pro XID zvýšen 0:neznámý

  2. Použití sloupce v SQL join bez jeho přidání do skupiny podle klauzule

  3. Jak zjednodušit výběrový dotaz, který obsahuje mnoho vnitřních výběrů, a zvýšit výkon v PostgreSQL

  4. Jak mohu zadat dotaz na všechny řádky v okruhu 5 mil od mých souřadnic?