Индекс Postgresql для выражения xpath не дает ускорения - PullRequest
4 голосов
/ 28 июня 2011

Мы пытаемся создать аналог OEBS в Postgresql.Допустим, у нас есть конструктор форм, и нам нужно хранить результаты формы в базе данных (например, тела электронной почты).В Oracle вы можете использовать таблицу с 150 ~ столбцами (и некоторые отображения хранятся в другом месте) для хранения каждого поля в отдельном столбце.Но, в отличие от Oracle, мы хотели бы хранить все формы в поле postgresql xml.Пример дерева:

 <row xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">  
   <object_id>2</object_id>  
   <pack_form_id>23</pack_form_id>  
   <prod_form_id>34</prod_form_id>
 </row>

Мы хотели бы найти это поле.Тестовая таблица содержит 400 тыс. Строк, и следующий выбор выполняется за 90 секунд:

select * 
from params 
where (xpath('//prod_form_id/text()'::text, xmlvalue))[1]::text::int=34

Итак, я создал этот индекс:

create index prod_form_idx 
ON params using btree(
   ((xpath('//prod_form_id/text()'::text, xmlvalue))[1]::text::int)
);

И это не имело никакого значения.Еще 90 секунд исполнения.Объясните план показать это:

Bitmap Heap Scan on params  (cost=40.29..6366.44 rows=2063 width=292)
  Recheck Cond: ((((xpath('//prod_form_id/text()'::text, xmlvalue, '{}'::text[]))[1])::text)::integer = 34)
  ->  Bitmap Index Scan on prod_form_idx  (cost=0.00..39.78 rows=2063 width=0)
        Index Cond: ((((xpath('//prod_form_id/text()'::text, xmlvalue, '{}'::text[]))[1])::text)::integer = 34)

Я не великий интерпретатор плана, поэтому я предполагаю, что это означает, что индекс используется.Вопрос: где вся скорость?И что я могу сделать, чтобы оптимизировать такого рода запросы?

1 Ответ

13 голосов
/ 28 июня 2011

Ну, по крайней мере, индекс используется.Вы получаете сканирование индекса растрового изображения вместо обычного сканирования индекса, что означает, что функция 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

Как мы видим,

  • сканирование всей таблицы с количеством (*) занимает 25 мс
  • извлечение одного ключа / значения из хранилища добавляет небольшую дополнительную стоимость, около 0,12 мкс / строка
  • извлечение одного ключа / значения из xml с использованием xpath добавляет огромные затраты, около 33 мкс / строка

Выводы:

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

Кроме того, поскольку ваши xml-данные довольно большие, они будут поджарены (сжаты и сохранены вне основной таблицы).Это делает строки в основной таблице намного меньше, а значит, на каждой странице больше строк, что снижает эффективность растровых сканирований, поскольку все строки на странице должны быть перепроверены.

Вы можете исправить это.По какой-то причине функция xpath () (которая очень медленная, поскольку она обрабатывает xml) имеет такую ​​же стоимость (1 единицу), как, скажем, целочисленный оператор "+" ...

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

Вы можетенужно настроить стоимость стоимости.Получив правильную информацию, планировщик знает, что xpath медленный, и будет избегать сканирования индекса растрового изображения, используя вместо этого сканирование индекса, которое не требует повторной проверки условия для всех строк на странице.

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...