PostgreSQL
 sql >> база данни >  >> RDS >> PostgreSQL

Postgresql индекс на xpath израз не дава ускорение

Е, поне индексът се използва. Все пак получавате сканиране на растерни индекси вместо нормално сканиране на индекси, което означава, че функцията xpath() ще бъде извиквана много пъти.

Нека направим малка проверка:

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

Както виждаме,

  • сканирането на цялата таблица с count(*) отнема 25 ms
  • извличането на един ключ/стойност от hstore добавя малка допълнителна цена, около 0,12 µs/ред
  • извличането на един ключ/стойност от xml чрез xpath добавя огромни разходи, около 33 µs/ред

Заключения :

  • xml е бавен (но всеки знае това)
  • ако искате да поставите гъвкаво хранилище за ключ/стойност в колона, използвайте hstore

Освен това, тъй като вашите xml данни са доста големи, те ще бъдат препечени (компресирани и съхранени извън основната таблица). Това прави редовете в главната таблица много по-малки, следователно повече редове на страница, което намалява ефективността на растерните сканирания, тъй като всички редове на страницата трябва да бъдат проверени отново.

Можете обаче да поправите това. По някаква причина функцията xpath() (която е много бавна, тъй като обработва xml) има същата цена (1 единица) като, да кажем, операторът за цяло число "+"...

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

Може да се наложи да промените стойността на разходите. Когато получи правилната информация, плановият знае, че xpath е бавен и ще избегне сканиране на индекс на растерна графика, като вместо това използва сканиране на индекс, което не изисква повторна проверка на условието за всички редове на страница.

Обърнете внимание, че това не решава проблема с оценките на редовете. Тъй като не можете да АНАЛИЗИРАТЕ вътрешността на xml (или hstore), получавате оценки по подразбиране за броя на редовете (тук 500). Така че планиращият може да греши напълно и да избере катастрофален план, ако са включени някои присъединявания. Единственото решение за това е да използвате правилни колони.




  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Не може да се създаде заявена услуга [org.hibernate.engine.jdbc.env.spi.JdbcEnvironment]

  2. Как да получите броя на INNER присъединяването и броя на броя на всички елементи?

  3. django.db.utils.ProgrammingError:връзката вече съществува

  4. Не може да се инсталира plpython3u - postgresql

  5. Комбинирайте множество редове с различни дати с припокриващи се променливи (за улавяне на първата и последната дата на промяна)