Наша система записывает много данных (вид системы Big Data). Производительность записи достаточно для наших нужд, но производительность чтения слишком низкая.
Структура первичного ключа (ограничения) одинакова для всех наших таблиц:
timestamp(Timestamp) ; index(smallint) ; key(integer).
Таблица может содержать миллионы строк, даже миллиарды строк, и запрос на чтение обычно относится к определенному периоду (отметка времени / индекс) и тегу. Распространено иметь запрос, который возвращает около 200 тыс. Строк. В настоящее время мы можем читать около 15 тыс. Строк в секунду, но нам нужно быть в 10 раз быстрее. Возможно ли это, и если да, то как?
Примечание: PostgreSQL поставляется с нашим программным обеспечением, поэтому аппаратное обеспечение отличается от одного клиента к другому.
Это виртуальная машина, используемая для тестирования. Хост виртуальной машины - Windows Server 2008 R2 x64 с 24,0 ГБ ОЗУ.
Спецификация сервера (виртуальная машина VMWare)
Server 2008 R2 x64
2.00 GB of memory
Intel Xeon W3520 @ 2.67GHz (2 cores)
postgresql.conf
Оптимизации
shared_buffers = 512MB (default: 32MB)
effective_cache_size = 1024MB (default: 128MB)
checkpoint_segment = 32 (default: 3)
checkpoint_completion_target = 0.9 (default: 0.5)
default_statistics_target = 1000 (default: 100)
work_mem = 100MB (default: 1MB)
maintainance_work_mem = 256MB (default: 16MB)
Определение таблицы
CREATE TABLE "AnalogTransition"
(
"KeyTag" integer NOT NULL,
"Timestamp" timestamp with time zone NOT NULL,
"TimestampQuality" smallint,
"TimestampIndex" smallint NOT NULL,
"Value" numeric,
"Quality" boolean,
"QualityFlags" smallint,
"UpdateTimestamp" timestamp without time zone, -- (UTC)
CONSTRAINT "PK_AnalogTransition" PRIMARY KEY ("Timestamp" , "TimestampIndex" , "KeyTag" ),
CONSTRAINT "FK_AnalogTransition_Tag" FOREIGN KEY ("KeyTag")
REFERENCES "Tag" ("Key") MATCH SIMPLE
ON UPDATE NO ACTION ON DELETE NO ACTION
)
WITH (
OIDS=FALSE,
autovacuum_enabled=true
);
запрос
Выполнение запроса в pgAdmin3 занимает около 30 секунд, но мы бы хотели получить тот же результат в течение 5 секунд, если это возможно.
SELECT
"AnalogTransition"."KeyTag",
"AnalogTransition"."Timestamp" AT TIME ZONE 'UTC',
"AnalogTransition"."TimestampQuality",
"AnalogTransition"."TimestampIndex",
"AnalogTransition"."Value",
"AnalogTransition"."Quality",
"AnalogTransition"."QualityFlags",
"AnalogTransition"."UpdateTimestamp"
FROM "AnalogTransition"
WHERE "AnalogTransition"."Timestamp" >= '2013-05-16 00:00:00.000' AND "AnalogTransition"."Timestamp" <= '2013-05-17 00:00:00.00' AND ("AnalogTransition"."KeyTag" = 56 OR "AnalogTransition"."KeyTag" = 57 OR "AnalogTransition"."KeyTag" = 58 OR "AnalogTransition"."KeyTag" = 59 OR "AnalogTransition"."KeyTag" = 60)
ORDER BY "AnalogTransition"."Timestamp" DESC, "AnalogTransition"."TimestampIndex" DESC
LIMIT 500000;
Объясните 1
"Limit (cost=0.00..125668.31 rows=500000 width=33) (actual time=2.193..3241.319 rows=500000 loops=1)"
" Buffers: shared hit=190147"
" -> Index Scan Backward using "PK_AnalogTransition" on "AnalogTransition" (cost=0.00..389244.53 rows=1548698 width=33) (actual time=2.187..1893.283 rows=500000 loops=1)"
" Index Cond: (("Timestamp" >= '2013-05-16 01:00:00-04'::timestamp with time zone) AND ("Timestamp" <= '2013-05-16 15:00:00-04'::timestamp with time zone))"
" Filter: (("KeyTag" = 56) OR ("KeyTag" = 57) OR ("KeyTag" = 58) OR ("KeyTag" = 59) OR ("KeyTag" = 60))"
" Buffers: shared hit=190147"
"Total runtime: 3863.028 ms"
Объяснить 2
В моем последнем тесте мои данные заняли 7 минут! Увидеть ниже:
"Limit (cost=0.00..313554.08 rows=250001 width=35) (actual time=0.040..410721.033 rows=250001 loops=1)"
" -> Index Scan using "PK_AnalogTransition" on "AnalogTransition" (cost=0.00..971400.46 rows=774511 width=35) (actual time=0.037..410088.960 rows=250001 loops=1)"
" Index Cond: (("Timestamp" >= '2013-05-22 20:00:00-04'::timestamp with time zone) AND ("Timestamp" <= '2013-05-24 20:00:00-04'::timestamp with time zone) AND ("KeyTag" = 16))"
"Total runtime: 411044.175 ms"
источник
Итак, из планов я вижу одну вещь: ваш индекс либо раздут (затем вместе с базовой таблицей), либо просто не очень хорош для такого рода запросов (я попытался рассмотреть это в своем последнем комментарии выше).
Одна строка индекса содержит 14 байтов данных (а некоторые для заголовка). Теперь, исходя из чисел, указанных в плане: вы получили 500 000 строк на 190147 страницах - это означает, в среднем, менее 3 полезных строк на странице, то есть около 37 байтов на страницу 8 КБ. Это очень плохое соотношение, не так ли? Поскольку первый столбец индекса является
Timestamp
полем и используется в запросе в качестве диапазона, планировщик может - и делает - выбрать индекс, чтобы найти совпадающие строки. НоTimestampIndex
вWHERE
условиях ничего не упоминается , поэтому фильтрацияKeyTag
не очень эффективна, так как эти значения предположительно появляются случайным образом на страницах индекса.Итак, одна возможность - это изменение определения индекса на
(или, учитывая загрузку вашей системы, создайте этот индекс как новый:
Другая возможность, что большая часть страниц индекса занята мертвыми строками, которые можно удалить путем очистки. Вы создали таблицу с настройкой
autovacuum_enabled=true
- но вы когда-нибудь начинали автоочистку? Или запуститьVACUUM
вручную?источник