2016-01-21 3 views
5

Я унаследовал большую устаревшую кодовую базу, которая работает в django 1.5, и моя текущая задача - ускорить раздел сайта, на который требуется загрузить ~ 1min.Улучшение скорости запроса: простой SELECT с LIKE

Я сделал профиль приложения и получил это:

enter image description here

Преступника, в частности, следующий запрос (раздел для краткости):

SELECT COUNT(*) FROM "entities_entity" WHERE (
    "entities_entity"."date_filed" <= '2016-01-21' AND (
    UPPER("entities_entity"."entity_city_state_zip"::text) LIKE UPPER('%Atherton%') OR 
    UPPER("entities_entity"."entity_city_state_zip"::text) LIKE UPPER('%Berkeley%') OR 
    -- 34 more of these 
    UPPER("entities_entity"."agent_city_state_zip"::text) LIKE UPPER('%Atherton%') OR 
    UPPER("entities_entity"."agent_city_state_zip"::text) LIKE UPPER('%Berkeley%') OR 
    -- 34 more of these 
) 
) 

, которые в основном состоят на большой как запрос на два полей, entity_city_state_zip и agent_city_state_zip которые character varying(200) | not null полей.

Этот запрос выполняется дважды (!), С 18814.02ms каждый раз, и один больше времени Замена COUNT для SELECT занимая дополнительное 20216.49 (я собираюсь кэшировать результат COUNT)

Explain, выглядит так:

Aggregate (cost=175867.33..175867.34 rows=1 width=0) (actual time=17841.502..17841.502 rows=1 loops=1) 
    -> Seq Scan on entities_entity (cost=0.00..175858.95 rows=3351 width=0) (actual time=0.849..17818.551 rows=145075 loops=1) 
     Filter: ((date_filed <= '2016-01-21'::date) AND ((upper((entity_city_state_zip)::text) ~~ '%ATHERTON%'::text) OR (upper((entity_city_state_zip)::text) ~~ '%BERKELEY%'::text) (..skipped..) OR (upper((agent_city_state_zip)::text) ~~ '%ATHERTON%'::text) OR (upper((agent_city_state_zip)::text) ~~ '%BERKELEY%'::text) OR (upper((agent_city_state_zip)::text) ~~ '%BURLINGAME%'::text))) 
     Rows Removed by Filter: 310249 
Planning time: 2.110 ms 
Execution time: 17841.944 ms 

Я попытался с помощью индекса на entity_city_state_zip и agent_city_state_zip с использованием различн s:

CREATE INDEX ON entities_entity (upper(entity_city_state_zip)); 
CREATE INDEX ON entities_entity (upper(agent_city_state_zip)); 

или с использованием varchar_pattern_ops, не повезло.

Сервер использует что-то вроде этого:

qs = queryset.filter(Q(entity_city_state_zip__icontains = all_city_list) | 
        Q(agent_city_state_zip__icontains = all_city_list)) 

для создания этого запроса.

Я не знаю, что еще попробовать,

Спасибо!

+2

'LIKE' запросов, который начинается с' '% ...' 'не будет использовать любой индекс ВТКЕЕ (включая 'xxx_pattern_ops'). Эти индексы выбирались только в том случае, если шаблон совпадает с началом. (f.ex. 'col LIKE 'XXX%'' или 'col ~ '^ XXX''). Вы можете попробовать ['pg_trgm' module] (http://www.postgresql.org/docs/current/static/pgtrgm.html), [который предоставляет подходящий вам индекс] (http: //dba.stackexchange. ком/вопросы/10694/шаблон сопоставление с подобным автомодельными к или-регулярным выражениям-в-PostgreSQL/10696). (и вы можете использовать 'ilike' вместо' like' и 'lower()'/'upper()' вызовов). – pozs

+0

@pozs Я этого не знал! Я дам ему попробовать – NicoSantangelo

+0

Я бы хотя бы хотел знать, какое влияние оказал «Seq Scan», и можно ли заменить индексное сканирование. Посмотрите, какой эффект «set enable_seqscan = false» имеет в плане. Разве БД работает от SSD? –

ответ

1

Я думаю, проблема в "multiple LIKE" и в UPPER ("entities_entity ...

Вы можете использовать:

WHERE entities_entity.entity_city_state_zip SIMILAR TO '%Atherton%|%Berkeley%'

Или что-то вроде этого:

WHERE entities_entity.entity_city_state_zip LIKE ANY(ARRAY['%Atherton%', '%Berkeley%'])


Отредактировано

О Raw SQL запроса в Django:

  1. https://docs.djangoproject.com/es/1.9/topics/db/sql/
  2. How do I execute raw SQL in a django migration

С уважением

+0

Я не знал, что 'LIKE' поддерживается' ANY' с массивом в качестве значения. Проблема, с которой я сталкиваюсь, заключается в том, чтобы создать django' для создания этого запроса. Я буду искать Google, чтобы узнать, что я могу найти – NicoSantangelo

+0

Это поддерживается postgres man) Как насчет django ... Я думаю, что «сырой запрос» - это то, что вы хотите https: //docs.djangoproject.com/es/1.9/topics/db/sql/ и также прочитайте эту ссылку http://stackoverflow.com/questions/31698103/how-do-i-execute-raw-sql-in-a -django-migration –

1

Я смотрел курс Pluralsight, что адресованное очень похожий вопрос. Курс был «Postgres for .NET Developers», и это было в разделе «Fun With Simple SQL», «Полный текстовый поиск».

Резюмируя свое решение, используя свой пример:

Создать новый столбец в вашей таблице, который будет представлять ваши entity_city_state_zip как поисковый вектор:

create table entities_entity (
    date_filed date, 
    entity_city_state_zip text, 
    csz_search tsvector not null -- add this column 
); 

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

update entities_entity 
set csz_search = to_tsvector (entity_city_state_zip); 

Далее создайте триггер, который будет вызывать новое поле заселяться в любое время, когда добавляется запись или изменение:

create trigger entities_insert_update 
before insert or update on entities_entity 
for each row execute procedure 
tsvector_update_trigger(csz_search,'pg_catalog.english',entity_city_state_zip); 

Ваши поисковые запросы могут теперь запрос на поле поискового вектора, а не город/штат/почтовый поле:

select * from entities_entity 
where csz_search @@ to_tsquery('Atherton') 

Некоторые ноты интерес по этому поводу:

  • to_tsquery, в случае, если вы не использовали его, ПУТЬ более сложна, чем пример выше. Это позволяет и условия, частичные совпадения, и т.д.
  • также нечувствительны к регистру, поэтому нет необходимости делать upper функции, которые вы имеете в своем запросе

В качестве последнего шага, поставить GIN индекс на поле tsquery:

create index entities_entity_ix1 on entities_entity 
using gin(csz_search); 

Если я понимаю, право конечно, это должно сделать свой муху запросов, и она преодолеет вопрос о неспособности индекса ВТКЕЯ к работе на like '% запроса.

Вот объяснить план на такой запрос:

Bitmap Heap Scan on entities_entity (cost=56.16..1204.78 rows=505 width=81) 
    Recheck Cond: (csz_search @@ to_tsquery('Atherton'::text)) 
    -> Bitmap Index Scan on entities_entity_ix1 (cost=0.00..56.04 rows=505 width=0) 
     Index Cond: (csz_search @@ to_tsquery('Atherton'::text)) 
+0

Это действительно здорово, я попробую, как только смогу – NicoSantangelo

+0

Это довольно здорово. Я провел несколько быстрых тестов на около 2 000 000 строк данных, и этот метод занял около 300 мс против около 2,4 секунд для традиционного запроса. С вложенными «или» запросами в более крупных наборах данных, я буду держать пари, что различия будут намного более драматичными. – Hambone

Смежные вопросы