У меня есть таблица с 7,2 миллиона кортежей, которая выглядит следующим образом:
table public.methods
column | type | attributes
--------+-----------------------+----------------------------------------------------
id | integer | not null DEFAULT nextval('methodkey'::regclass)
hash | character varying(32) | not null
string | character varying | not null
method | character varying | not null
file | character varying | not null
type | character varying | not null
Indexes:
"methods_pkey" PRIMARY KEY, btree (id)
"methodhash" btree (hash)
Теперь я хочу выбрать некоторые значения, но запрос невероятно медленный:
db=# explain
select hash, string, count(method)
from methods
where hash not in
(select hash from nostring)
group by hash, string
order by count(method) desc;
QUERY PLAN
----------------------------------------------------------------------------------------
Sort (cost=160245190041.10..160245190962.07 rows=368391 width=182)
Sort Key: (count(methods.method))
-> GroupAggregate (cost=160245017241.77..160245057764.73 rows=368391 width=182)
-> Sort (cost=160245017241.77..160245026451.53 rows=3683905 width=182)
Sort Key: methods.hash, methods.string
-> Seq Scan on methods (cost=0.00..160243305942.27 rows=3683905 width=182)
Filter: (NOT (SubPlan 1))
SubPlan 1
-> Materialize (cost=0.00..41071.54 rows=970636 width=33)
-> Seq Scan on nostring (cost=0.00..28634.36 rows=970636 width=33)
hash
Колонна является md5 хэш string
и имеет индекс. Поэтому я думаю, что моя проблема в том, что вся таблица сортируется по идентификатору, а не по хешу, поэтому сначала нужно отсортировать ее, а затем сгруппировать?
Таблица nostring
содержит только список хэшей, которые я не хочу иметь. Но мне нужно, чтобы обе таблицы имели все значения. Так что это не вариант, чтобы удалить их.
дополнительная информация: ни один из столбцов не может быть нулевым (исправлено в определении таблицы), и я использую postgresql 9.2.
NULL
значений в столбцеmethod
? Есть ли дубликаты наstring
?Ответы:
LEFT JOIN
В @ Dezső Ответим должно быть хорошо. Индекс, однако, вряд ли будет полезен (сам по себе), потому что в любом случае запрос должен прочитать всю таблицу - исключение составляет сканирование только по индексу в Postgres 9.2+ и благоприятные условия, см. Ниже.Запустите
EXPLAIN ANALYZE
по запросу. Несколько раз, чтобы исключить эффекты обналичивания и шума. Сравните лучшие результаты.Создайте многостолбцовый индекс, соответствующий вашему запросу:
Подождите? После того, как я сказал, что индекс не поможет? Ну, нам нужно это к
CLUSTER
столу:Rerun
EXPLAIN ANALYZE
. Еще быстрее? Так должно быть.CLUSTER
это однократная операция по перезаписи всей таблицы в порядке используемого индекса. Это также эффективноVACUUM FULL
. Если вы хотите быть уверенным, вы должны провести предварительное тестирование сVACUUM FULL
одним, чтобы увидеть, что может быть связано с этим.Если ваша таблица видит много операций записи, эффект со временем ухудшится. Расписание
CLUSTER
в нерабочее время для восстановления эффекта. Точная настройка зависит от вашего конкретного случая использования. Руководство оCLUSTER
.CLUSTER
это довольно грубый инструмент, нуждается в эксклюзивном замке на столе. Если вы не можете себе этого позволить, подумайте,pg_repack
что можно сделать без исключительной блокировки. Подробнее в этом позже ответ:Если процент
NULL
значений в столбцеmethod
высокий (более ~ 20 процентов, в зависимости от фактических размеров строки), частичный индекс должен помочь:(Ваше последующее обновление показывает, что ваши столбцы будут
NOT NULL
, поэтому не применимо.)Если вы используете PostgreSQL 9.2 или новее (как прокомментировал @deszo ), представленные индексы могут быть полезны без
CLUSTER
использования планировщика, который может использовать сканирование только по индексу . Применяется только при благоприятных условиях:VACUUM
индекс не должен охватывать операции записи, которые влияли бы на карту видимости, поскольку последний и все столбцы в запросе должны быть покрыты. В основном таблицы только для чтения могут использовать это в любое время, в то время как сильно написанные таблицы ограничены. Более подробная информация в Postgres Wiki.Вышеупомянутый частичный индекс может быть еще более полезным в этом случае.
Если , с другой стороны, в столбце нет
NULL
значенийmethod
, вы должны1.) определить его
NOT NULL
и2.) использовать
count(*)
вместоcount(method)
, это немного быстрее и делает то же самое при отсутствииNULL
значений.Если вам приходится часто вызывать этот запрос и таблица доступна только для чтения, создайте
MATERIALIZED VIEW
.Экзотическая тонкость: ваша таблица названа
nostring
, но, похоже, содержит хэши. Исключая хеши вместо строк, есть вероятность, что вы исключите больше строк, чем предполагалось. Крайне маловероятно, но возможно.источник
Добро пожаловать в DBA.SE!
Вы можете попытаться перефразировать ваш запрос следующим образом:
или другая возможность:
NOT IN
типичный приемник производительности, поскольку с ним сложно использовать индекс.Это может быть дополнительно улучшено с помощью индексов. Индекс на
nostring.hash
внешности полезен. Но сначала: что вы получаете сейчас? (Было бы лучше увидеть результат,EXPLAIN ANALYZE
так как сами затраты не указывают время, затраченное на операции.)источник
EXPLAIN ANALYZE
.Поскольку хэш - это md5, вы, вероятно, можете попытаться преобразовать его в число: вы можете сохранить его как число или просто создать функциональный индекс, который вычисляет это число в неизменяемой функции.
Другие люди уже создали функцию pl / pgsql, которая преобразовывает (часть) значение md5 из текста в строку. Смотрите пример /programming/9809381/hashing-a-string-to-a-numeric-value-in-postgressql
Я считаю, что вы действительно тратите много времени на сравнение строк при сканировании индекса. Если вам удастся сохранить это значение как число, тогда оно должно быть действительно очень быстрым.
источник
Я много сталкивался с этой проблемой и обнаружил простой трюк из двух частей.
Создайте индекс подстроки по хеш-значению: (обычно длина 7)
create index methods_idx_hash_substring ON methods(substring(hash,1,7))
Ваши поиски / объединения содержат совпадение подстрок, поэтому планировщику запросов намекают на использование индекса:
старый:
WHERE hash = :kwarg
новые:
WHERE (hash = :kwarg) AND (substring(hash,1,7) = substring(:kwarg,1,7))
Вы также должны иметь индекс по сырью
hash
.В результате (обычно) планировщик сначала сверится с индексом подстроки и отфильтрует большинство строк. затем он сопоставляет полный 32-х символьный хэш с соответствующим индексом (или таблицей). этот подход снизил 800 мс запросов до 4 для меня.
источник