¿Podría alguien explicarme este comportamiento? Ejecuté la siguiente consulta en Postgres 9.3 ejecutándose de forma nativa en OS X. Intenté simular algún comportamiento en el que el tamaño del índice podría crecer mucho más que el tamaño de la tabla, y en su lugar encontré algo aún más extraño.
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);
Dejé que esto se ejecute durante aproximadamente una hora en mi máquina local, antes de comenzar a recibir advertencias de problemas de disco de OS X. Noté que Postgres estaba absorbiendo aproximadamente 10 MB / s de mi disco local, y que la base de datos de Postgres estaba consumiendo un gran total de 30GB desde mi máquina. Terminé cancelando la consulta. De todos modos, Postgres no me devolvió el espacio en disco y pregunté a la base de datos las estadísticas de uso con el siguiente resultado:
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
Sin embargo, la selección de la tabla no arrojó resultados.
test=# select * from test limit 1;
id
----
(0 rows)
Ejecutar 10000 lotes de 500 es 5,000,000 filas, lo que debería producir un tamaño de tabla / índice bastante pequeño (en la escala de MB). Sospecho que Postgres está creando una nueva versión de la tabla / índice para cada INSERTAR / ACTUALIZAR que sucede con la función, pero esto parece extraño. Toda la función se ejecuta transaccionalmente y la tabla estaba vacía para comenzar.
¿Alguna idea de por qué estoy viendo este comportamiento?
Específicamente, las dos preguntas que tengo son: ¿por qué este espacio aún no ha sido reclamado por la base de datos y el segundo es por qué la base de datos requirió tanto espacio en primer lugar? 30 GB parece mucho, incluso cuando se cuenta con MVCC
fuente
Los números reales después de analizar la función son mucho más grandes porque todas las filas de la tabla obtienen el mismo valor que se actualiza varias veces en cada iteración.
Cuando lo ejecutamos con parámetros
n
ym
:Hay
m
inserciones de fila yn * (m^2 + m) / 2
actualizaciones. Entonces, paran = 500
ym = 10000
, Postgres necesitará insertar solo 10K filas pero realizar ~ 25G (25 mil millones) de actualizaciones de tuplas.Teniendo en cuenta que una fila en Postgres tiene unos 24 bytes de sobrecarga, una tabla con una sola
int
columna necesitará 28 bytes por fila más la sobrecarga de la página. Entonces, para que la operación finalice, necesitaríamos alrededor de 700 GB más el espacio para el índice (que también serían unos pocos cientos de GB).Pruebas
Para probar la teoría, creamos otra tabla
test_test
con una sola fila.Luego agregamos un activador
test
para que cada actualización aumente el contador en 1. (Código omitido). Luego ejecutamos la función, con valores más pequeños,n = 50
ym = 100
.Nuestra teoría predice :
Prueba 1 (
test
tabla original , con índice)Después de completar, verificamos el contenido de la tabla:
Y uso del disco (consulta en Tamaño del índice / estadísticas de uso en Mantenimiento del índice ):
La
test
tabla ha usado casi 9 MB para la tabla y 5 MB para el índice. ¡Tenga en cuenta que latest_test
tabla ha usado otros 9MB! Eso es esperado ya que también pasó por 250K actualizaciones (nuestro segundo disparador actualizó la fila individual detest_test
cada actualización de una fila entest
).Tenga en cuenta también el número de escaneo en la mesa
test
(10K) y las lecturas de tuplas (500K).Prueba 2 (
test
tabla sin índice)Exactamente igual que el anterior, excepto que la tabla no tiene índice.
Obtenemos el mismo tamaño para el uso del disco de la tabla y, por supuesto, ningún uso del disco para los índices. Sin
test
embargo, el número de escaneos en la tabla es cero y las tuplas también se leen.Prueba 3 (con factor de relleno inferior)
Probado con el factor de relleno 50 y el más bajo posible, 10. Ninguna mejora en absoluto. El uso del disco era casi idéntico a las pruebas anteriores (que usaban el factor de relleno predeterminado, 100 por ciento)
fuente