Может ли кто-нибудь объяснить мне это поведение? Я выполнил следующий запрос на Postgres 9.3, работающем изначально на OS X. Я пытался смоделировать какое-то поведение, когда размер индекса мог вырасти намного больше размера таблицы, и вместо этого нашел что-то еще более странное.
CREATE TABLE test(id int);
CREATE INDEX test_idx ON test(id);
CREATE FUNCTION test_index(batch_size integer, total_batches integer) RETURNS void AS $$
DECLARE
current_id integer := 1;
BEGIN
FOR i IN 1..total_batches LOOP
INSERT INTO test VALUES (current_id);
FOR j IN 1..batch_size LOOP
UPDATE test SET id = current_id + 1 WHERE id = current_id;
current_id := current_id + 1;
END LOOP;
END LOOP;
END;
$$ LANGUAGE plpgsql;
SELECT test_index(500, 10000);
Я позволил этой программе работать на моем локальном компьютере около часа, прежде чем я начал получать предупреждения о проблемах диска из OS X. Я заметил, что Postgres высасывает со своего локального диска около 10 МБ / с и что база данных Postgres потребляет общую сумму. 30 ГБ с моей машины. Я закончил тем, что отменил запрос. Несмотря на это, Postgres не вернул мне дисковое пространство, и я запросил базу данных для статистики использования со следующим результатом:
test=# SELECT nspname || '.' || relname AS "relation",
pg_size_pretty(pg_relation_size(C.oid)) AS "size"
FROM pg_class C
LEFT JOIN pg_namespace N ON (N.oid = C.relnamespace)
WHERE nspname NOT IN ('pg_catalog', 'information_schema')
ORDER BY pg_relation_size(C.oid) DESC
LIMIT 20;
relation | size
-------------------------------+------------
public.test | 17 GB
public.test_idx | 14 GB
Однако выбор из таблицы не дал результатов.
test=# select * from test limit 1;
id
----
(0 rows)
Выполнение 10000 пакетов по 500 означает 5 000 000 строк, что должно привести к довольно небольшому размеру таблицы / индекса (в масштабе МБ). Я подозреваю, что Postgres создает новую версию таблицы / индекса для каждого INSERT / UPDATE, что происходит с функцией, но это кажется странным. Вся функция выполняется транзакционно, и таблица была пуста для запуска.
Есть мысли о том, почему я вижу это поведение?
В частности, у меня есть два вопроса: почему эта область еще не была восстановлена базой данных, и второй - почему база данных потребовала столько места в первую очередь? 30 ГБ кажется много, даже если учитывать MVCC
источник
Фактические числа после анализа функции намного больше, потому что все строки таблицы получают одно и то же значение, которое обновляется несколько раз в каждой итерации.
Когда мы запускаем его с параметрами
n
иm
:Есть
m
строки вставки иn * (m^2 + m) / 2
обновления. Таким образом, дляn = 500
иm = 10000
Postgres нужно будет вставить только 10K строк, но выполнить обновления кортежей ~ 25G (25 миллиардов).Учитывая, что строка в Postgres имеет около 24 байтов служебной информации, таблице с одним
int
столбцом потребуется 28 байтов на строку плюс накладные расходы страницы. Таким образом, для завершения операции нам потребуется около 700 ГБ плюс место для индекса (что также будет несколько сотен ГБ).тестирование
Чтобы проверить теорию, мы создали еще одну таблицу
test_test
с одной строкой.Затем мы добавляем триггер,
test
чтобы каждое обновление увеличивало счетчик на 1. (Код опущен). Затем мы запускаем функцию, с меньшими значениями,n = 50
иm = 100
.Наша теория предсказывает :
Тест 1 (оригинальная
test
таблица, с индексом)После завершения мы проверяем содержимое таблицы:
И использование диска (запрос в разделе Размер индекса / Статистика использования в обслуживании индекса ):
test
Таблица используется почти 9MB для таблицы и 5МБ для индекса. Обратите внимание, чтоtest_test
таблица использовала еще 9 МБ! Это ожидаемо, поскольку оно также прошло 250K обновлений (наш второй триггер обновлял одну строкуtest_test
для каждого обновления строки вtest
.)Обратите внимание также на количество сканированных таблиц
test
(10 КБ) и число записей (500 КБ).Тест 2 (
test
таблица без индекса)Точно так же, как и выше, за исключением того, что таблица не имеет индекса.
Мы получаем одинаковый размер для использования диска в таблице и, конечно, для использования в дисках для индексов. Число сканирований на столе
test
равно нулю, и кортежи тоже читаются.Тест 3 (с нижним коэффициентом заполнения)
Пробовал с fillfactor 50 и минимально возможным, 10. Никаких улучшений вообще. Использование диска было практически идентично предыдущим тестам (которые использовали коэффициент заполнения по умолчанию, 100 процентов)
источник