PostgreSQL
 sql >> Baza danych >  >> RDS >> PostgreSQL

Indeks Postgresql na wyrażeniu xpath nie przyspiesza

Cóż, przynajmniej używa się indeksu. Otrzymasz jednak skanowanie indeksu bitmapy zamiast normalnego skanowania indeksu, co oznacza, że ​​funkcja xpath() będzie wywoływana wiele razy.

Zróbmy mały test :

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 widać,

  • skanowanie całej tabeli z count(*) zajmuje 25 ms
  • pobranie jednego klucza/wartości z hstore powoduje niewielki dodatkowy koszt, około 0,12 µs/wiersz
  • wyodrębnienie jednego klucza/wartości z pliku xml za pomocą xpath dodaje ogromny koszt, około 33 µs/wiersz

Wnioski :

  • xml działa wolno (ale wszyscy o tym wiedzą)
  • jeśli chcesz umieścić elastyczny magazyn klucz/wartość w kolumnie, użyj hstore

Ponadto, ponieważ twoje dane xml są dość duże, zostaną one upieczone (skompresowane i zapisane poza główną tabelą). To sprawia, że ​​wiersze w głównej tabeli są znacznie mniejsze, stąd więcej wierszy na stronę, co zmniejsza wydajność skanowania bitmap, ponieważ wszystkie wiersze na stronie muszą zostać ponownie sprawdzone.

Możesz to jednak naprawić. Z jakiegoś powodu funkcja xpath() (która jest bardzo powolna, ponieważ obsługuje xml) ma taki sam koszt (1 jednostka) jak powiedzmy operator liczb całkowitych "+"...

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

Może być konieczne dostosowanie wartości kosztu. Po otrzymaniu właściwych informacji planista wie, że xpath jest powolny i uniknie skanowania indeksu bitmapy, używając zamiast tego skanowania indeksu, które nie wymaga ponownego sprawdzania warunku dla wszystkich wierszy na stronie.

Zwróć uwagę, że to nie rozwiązuje problemu szacowania wierszy. Ponieważ nie możesz ANALIZOWAĆ wnętrza xml (lub hstore), otrzymujesz domyślne szacunki liczby wierszy (tutaj 500). Tak więc planista może się całkowicie mylić i wybrać plan katastroficzny, jeśli w grę wchodzą niektóre połączenia. Jedynym rozwiązaniem tego problemu jest użycie odpowiednich kolumn.




  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. WpiszORM PostgreSQL select, gdzie pole JSON jest równe pewnej wartości

  2. Zmiana ORDER BY z id na inną indeksowaną kolumnę (z niskim LIMIT) ma ogromny koszt

  3. Jak mogę napisać to zapytanie postgres w Amazon redshift tak, aby było tak zoptymalizowane, jak w postgresie?

  4. Skonfiguruj `DataSource`, aby połączyć się z zarządzanym serwerem Postgres w Digital Ocean z szyfrowaniem SSL/TLS

  5. dynamiczne zapytanie sql w postgresie