У меня есть таблица, над которой я работаю, в которой около 3 миллионов кортежей. Он не меняется слишком часто (несколько обновлений или вставок в неделю) и часто читается. (Пожалуйста, не комментируйте varchar длины 1. Я знаю, я знаю).
Column | Type | Modifiers
-------------+-----------------------+------------------------------------------------------
id | integer | not null default nextval('mytable_id_seq'::regclass)
A | character varying(5) | not null
B | character varying(16) | not null
C | character varying(3) | not null
D | character varying(1) | not null
otherdata | character varying(99) | not null
Indexes:
"mytable_pkey" PRIMARY KEY, btree (id)
"mytable_unique_key" UNIQUE, btree (A, B, C, D)
"mytable_B_idx" btree (B)
Foreign-key constraints:
"$1" FOREIGN KEY (A, B) REFERENCES anothertable1(A, B)
"$2" FOREIGN KEY (C) REFERENCES anothertable2(C)
"$3" FOREIGN KEY (D) REFERENCES anothertable3(D)
Referenced by:
TABLE "anothertable4" CONSTRAINT "$1" FOREIGN KEY (id) REFERENCES mytable(id)
TABLE "anothertable5" CONSTRAINT "fkey_id" FOREIGN KEY (id) REFERENCES mytable(id) ON UPDATE CASCADE ON DELETE CASCADE
id
- мой первичный ключ. A,B,C,D
является ключом-кандидатом. Оба, очевидно, однозначно идентифицируют кортеж.
Наиболее частые запросы:
SELECT * FROM mytable WHERE B='foo';
- вернет количество кортежей
SELECT * FROM mytable WHERE A='foo' AND B='bar' AND C='baz' AND D='f';
- вернет один кортеж.
Следовательно, почему существуют индексы B
и A,B,C,D
.
Теперь по какой-то причине я делаю следующий запрос (и более похожий):
SELECT * FROM mytable WHERE ((A='foo' AND B='bar') OR (B='foo' AND C='bar'));
В одной коробке работает PostgreSQL 8.4.4. Если я ОБЪЯСНУЮ АНАЛИЗ первого запроса, я получу следующий план запроса:
QUERY PLAN
---------------------------------------------------------------------------------------------------------------------------------------------------------------
Bitmap Heap Scan on mytable (cost=9.74..174.30 rows=1 width=14) (actual time=0.000..0.000 rows=5 loops=1)
Recheck Cond: ((((A)::text = 'foo'::text) AND ((B)::text = 'bar'::text)) OR ((B)::text = 'foo'::text))
Filter: ((((A)::text = 'foo'::text) AND ((B)::text = 'bar'::text)) OR (((B)::text = 'foo'::text) AND ((C)::text = 'bar'::text)))
-> BitmapOr (cost=9.74..9.74 rows=42 width=0) (actual time=0.000..0.000 rows=0 loops=1)
-> Bitmap Index Scan on mytable_unique_key(cost=0.00..4.80 rows=1 width=0) (actual time=0.000..0.000 rows=0 loops=1)
Index Cond: (((A)::text = 'foo'::text) AND ((B)::text = 'bar'::text))
-> Bitmap Index Scan on mytable_B_idx(cost=0.00..4.94 rows=42 width=0) (actual time=0.000..0.000 rows=316 loops=1)
Index Cond: ((B)::text = 'foo'::text)
Total runtime: 0.000 ms
(9 rows)
Минимальная стоимость 9,74 и почти мгновенный возврат (Да, он кэшируется). Теперь, если я выполню тот же запрос на PostgreSQL 8.1.5 на другом похожем компьютере - с точно таким же содержимым в таблице - я получу следующее:
QUERY PLAN
---------------------------------------------------------------------------------------------------------------------------------------------------------------------
Bitmap Heap Scan on mytable (cost=110156.34..110168.36 rows=3 width=26) (actual time=147200.984..147221.480 rows=5 loops=1)
Recheck Cond: ((((A)::text = 'foo'::text) AND ((B)::text = 'bar'::text)) OR (((B)::text = 'foo'::text) AND ((C)::text = 'bar'::text)))
-> BitmapOr (cost=110156.34..110156.34 rows=3 width=0) (actual time=147185.513..147185.513 rows=0 loops=1)
-> Bitmap Index Scan on mytable_unique_key(cost=0.00..2.01 rows=1 width=0) (actual time=83.275..83.275 rows=0 loops=1)
Index Cond: (((A)::text = 'foo'::text) AND ((B)::text = 'bar'::text))
-> Bitmap Index Scan on mytable_unique_key(cost=0.00..110154.34 rows=2 width=0) (actual time=147102.230..147102.230 rows=5 loops=1)
Index Cond: (((B)::text = 'foo'::text) AND ((C)::text = 'bar'::text))
Total runtime: 147221.663 ms
(8 rows)
Обе таблицы были VACUUM и обе коробки. Таким образом, невероятная разница обусловлена различными версиями и повышением производительности, введенными между 8.1.5 и 8.4.4. Большой до разработчиков!
Хорошо, смысл этого вопроса не в том, чтобы сравнить различные версии PostgreSQL, а в том, чтобы спросить: как я могу улучшить производительность вышеупомянутого запроса? У меня есть следующие решения (или вопросы):
- Обновление до последней стабильной версии PostgreSQL. У нас 8.1.5 в производстве на многих серверах.
Против: задача обновления будет долгой. Я не возражаю слишком много, так как это будут делать оперативники. Данные потребуют полного дампа и импорта.
Pro: Мы получаем выгоду от безумного улучшения производительности и дополнительных функций, которые поставляются с последней версией.
- Оптимизируйте запрос, чтобы помочь планировщику. Я не вижу, как я могу сделать это для вышеуказанного запроса.
- Добавить индексы.
Это поможет планировщику и ускорит выполнение. Однако это добавляет немного накладных расходов. И какие индексы мне нужно будет добавить?
A,B
и B,C
или A
, B
и C
? Первый поможет с вышеуказанным запросом. Но у меня есть другие похожие запросы, которые фильтруют по другим столбцам. Запросы будут выполняться для следующих наборов столбцов: B
, B,C
, A,B
, A,B,C
, B,C,D
и A,B,C,D
. Означает ли это, что мне нужен индекс для каждого набора столбцов? Или просто самый дорогой? В приведенном выше запросе сканирование для B,C
было самым дорогим.
Заранее спасибо.