Quelqu'un pourrait-il m'expliquer ce comportement? J'ai exécuté la requête suivante sur Postgres 9.3 fonctionnant en mode natif sur OS X. J'essayais de simuler un comportement où la taille de l'index pouvait augmenter beaucoup plus que la taille de la table, et j'ai trouvé quelque chose de plus bizarre.
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);
J'ai laissé cela s'exécuter pendant environ une heure sur ma machine locale, avant de commencer à recevoir des avertissements de problème de disque d'OS X. J'ai remarqué que Postgres aspirait environ 10 Mo / s de mon disque local et que la base de données Postgres consommait un grand total de 30 Go de ma machine. J'ai fini par annuler la requête. Quoi qu'il en soit, Postgres ne m'a pas renvoyé l'espace disque et j'ai interrogé la base de données pour obtenir des statistiques d'utilisation avec le résultat suivant:
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
Cependant, la sélection dans le tableau n'a donné aucun résultat.
test=# select * from test limit 1;
id
----
(0 rows)
L'exécution de 10 000 lots de 500 correspond à 5 000 000 de lignes, ce qui devrait donner une taille de table / d'index assez petite (sur l'échelle de Mo). Je soupçonne que Postgres crée une nouvelle version de la table / index pour chaque INSERT / UPDATE qui se passe avec la fonction, mais cela semble étrange. La fonction entière est exécutée de manière transactionnelle et la table était vide pour démarrer.
Avez-vous des raisons de penser à ce comportement?
Plus précisément, les deux questions que je me pose sont les suivantes: pourquoi cet espace n'a-t-il pas encore été récupéré par la base de données et la seconde est pourquoi la base de données a-t-elle exigé autant d'espace en premier lieu? 30 Go semble beaucoup même en tenant compte de MVCC
la source
Les nombres réels après analyse de la fonction sont beaucoup plus importants car toutes les lignes du tableau ont la même valeur qui est mise à jour plusieurs fois à chaque itération.
Lorsque nous l'exécutons avec des paramètres
n
etm
:il y a des
m
insertions de lignes et desn * (m^2 + m) / 2
mises à jour. Ainsi, pourn = 500
etm = 10000
, Postgres devra insérer seulement 10K lignes mais effectuer des mises à jour de tuple ~ 25G (25 milliards).Étant donné qu'une ligne dans Postgres a une surcharge d'environ 24 octets, une table avec une seule
int
colonne aura besoin de 28 octets par ligne plus la surcharge de la page. Donc, pour que l'opération se termine, nous aurions besoin d'environ 700 Go plus l'espace pour l'index (qui serait également de quelques centaines de Go).Essai
Pour tester la théorie, nous avons créé une autre table
test_test
avec une seule ligne.Ensuite, nous ajoutons un déclencheur pour
test
que chaque mise à jour augmente le compteur de 1. (Code omis). Ensuite, nous nous exécutons la fonction, avec des valeurs plus petites,n = 50
etm = 100
.Notre théorie prédit :
Test 1 (
test
tableau d' origine , avec index)Après l'achèvement, nous vérifions le contenu du tableau:
Et l'utilisation du disque (requête sous Index size / usage statistics in Index Maintenance ):
La
test
table a utilisé près de 9 Mo pour la table et 5 Mo pour l'index. Notez que latest_test
table a utilisé un autre 9 Mo! Cela est attendu car il a également subi 250 000 mises à jour (notre deuxième déclencheur a mis à jour la ligne unique detest_test
chaque mise à jour d'une lignetest
).Notez également le nombre de numérisations sur table
test
(10K) et les lectures de tuples (500K).Test 2 (
test
tableau sans index)Exactement comme ci-dessus, sauf que la table n'a pas d'index.
Nous obtenons la même taille pour l'utilisation du disque de la table et bien sûr aucune utilisation du disque pour les index. Le nombre d'analyses sur la table
test
est cependant nul et les tuples se lisent également.Test 3 (avec facteur de remplissage inférieur)
Essayé avec fillfactor 50 et le plus bas possible, 10. Aucune amélioration du tout. L'utilisation du disque était presque identique aux tests précédents (qui utilisaient le facteur de remplissage par défaut, 100%)
la source