Comme vous l'avez peut-être remarqué, en tant que base de données entièrement gérée en tant que produit de service, le service de base de données relationnelle AWS (RDS) restreint l'exécution des commandes userland.
J'avais la mise en place d'une base de données de production et j'ai obtenu mon pg_largeobject
Table gonflée à 40% de la capacité du dispositif virtuel entière de persistance.
Comment puis-je courir vacuumlo
(joliment bien expliqué dans une autre question dba.se ICI ) sur une base de données PostgreSQL de RDS.
Trouver - ICI (dans le miroir GITHUB des Sources de vacuumlo
pour la version de base de données PostgreSQL J'utilisais à ce moment-là.
Vous pouvez imaginer le reste: je viens de imiter ce que le programme effectue, également simplement décrit ici .
Préparez la table temporaire des références d'objet ou des OID.
=> SET search_path = pg_catalog;
[...]
=> CREATE TABLE vacuum_lo_removeme AS \
SELECT oid AS lo FROM pg_largeobject_metadata;
[...]
=> ANALYZE vacuum_lo_removeme;
[...]
=> _
Effectuez la requête qui vous renvoie toutes vos colonnes de base de données saisies OID:
=> SELECT
s.nspname, c.relname, a.attname FROM pg_class c, pg_attribute a
, pg_namespace s, pg_type t
WHERE
a.attnum > 0 AND NOT a.attisdropped AND a.attrelid = c.oid
AND a.atttypid = t.oid AND c.relnamespace = s.oid
AND t.typname in ('oid', 'lo') AND c.relkind in ('r','m')
AND s.nspname !~ '^pg_';
Ensuite, vous devez exécuter cette requête pour tous les résultats obtenus par la requête antérieure (note ${VARIABLE}
est quelque chose que vous devez vous substituer en fonction de votre kilométrage) afin de supprimer de la table temporaire toutes les OID d'objets réellement utilisés. :
=> DELETE FROM vacuum_lo_removeme WHERE \
lo IN (SELECT ${column} FROM ${SCHEMA}.${TABLE});
Dans mon cas, il ne s'agissait que de deux tables totalement sur cinq colonnes et réellement des tables vides, si naturellement les cinq DELETE
requêtes ne faisaient rien. Si vous avez un plus grand OID activé Subschema Vous pourriez avoir besoin d'automatiser cela en quelque sorte.
Enfin, le programme déclare un curseur qui présente les cellules restantes lo
de la table temporaire, les purgeant avec un appel de fonction lo_unlink
.
J'aurais dû automatiser cela avec une procédure stockée PLPGSQL, mais depuis que je suis suce sur ce type de tâches, je viens de publier cette doublure:
$ echo 'SELECT COUNT(*) FROM vacuum_lo_removeme;' | \
$MY_AUTOMATABLE_PSQL_MILEAGE
count
---------
1117233
(1 row)
Ensuite, cet autre qui itière en sélectionnant le premier orphelin OID dans la table temporaire des OID orphelin, puis le désabonnement et le retirer de la table:
$ for i in {1..1117000}; do \
export oid=$(echo 'SELECT * FROM vacuum_lo_removeme LIMIT 1' | \
$MY_AUTOMATABLE_PSQL_MILEAGE | grep -v 'lo\|\-\-\-\-\|row\|^$' | \
sed s/\ //g) && \
echo "SELECT lo_unlink($oid); \
DELETE FROM vacuum_lo_removeme WHERE lo = $oid" | \
$MY_AUTOMATABLE_PSQL_MILEAGE; \
done
lo_unlink
-----------
1
(1 row)
DELETE 1
lo_unlink
-----------
1
(1 row)
DELETE 1
lo_unlink
-----------
1
(1 row)
DELETE 1
lo_unlink
-----------
1
(1 row)
DELETE 1
ERROR: must be owner of large object 18448
DELETE 1
ERROR: must be owner of large object 18449
DELETE 1
ERROR: must be owner of large object 18450
DELETE 1
ERROR: must be owner of large object 18451
[...]
lo_unlink
-----------
1
(1 row)
DELETE 1
[...]
Je savais que c'était sous-optimisé comme l'enfer, mais je l'ai laissé éliminer lentement ces archives orphelines. Lorsqu'on n'étant pas dba à tous ceux-ci, ces doublures peuvent être plus faciles à forger que de travailler de manière significative Idiomatic PLPGSQL.
Mais c'était trop lent pour laisser ça comme ça.
Vous serez en mesure d'accélérer un gros objet de dissimulation avec quelque chose de simple comme:
=> CREATE OR REPLACE FUNCTION unlink_Orphan_los() RETURNS VOID AS $$
DECLARE
iterator integer := 0;
largeoid OID;
myportal CURSOR FOR SELECT lo FROM vacuum_lo_removeme;
BEGIN
OPEN myportal;
LOOP
FETCH myportal INTO largeoid;
EXIT WHEN NOT FOUND;
PERFORM lo_unlink(largeoid);
DELETE FROM vacuum_lo_removeme WHERE lo = largeoid;
iterator := iterator + 1;
RAISE NOTICE '(%) removed lo %?', iterator, largeoid;
IF iterator = 100 THEN EXIT; END IF;
END LOOP;
END;$$LANGUAGE plpgsql;
[~ # ~] NOTE [~ # ~ ~] Il n'est pas nécessaire de dissuader les objets de grande taille 100
à la fois, pas même un numéro particulier x
d'entre eux À une époque, mais le dissociant 100
à la fois est la base la plus sûre applicable à la configuration de la mémoire par défaut de toutes les tailles d'instance AWS. Si vous utilisez un nombre excessivement grand pour cela, vous risquez une défaillance de la fonction en raison de la mémoire insuffisamment assignée; La façon dont vous pouvez faire dépendra de la quantité d'objets à dissuader et de leur taille, de type et de taille, ainsi que du degré de configuration supplémentaire manuelle appliquée.
Qui est un peu facile à forger pour une personne non DBA, puis l'appelant avec quelque chose comme
$ for i in {0..$WHATEVER}; do echo 'SELECT unlink_Orphan_los()' | \
$YOUR_AUTOMATABLE_PSQL_MILEAGE
Où ${WHATEVER}
est une constante construite en fonction de la taille de la table de gros objets temporaires et du nombre de serrures par transaction Votre configuration permet de permettre (j'utilise des paramètres par défaut de RDS, mais itération de bash
je suppose que je Vous n'êtes même pas obligé d'apprendre à savoir quel est le plus grand nombre de lo_unlink
s le RDBMS permet de connaître le courant max_locks_per_transaction
.
VACUUM
gros tables d'objet.Apparuré par ce fil dans la liste de diffusion Postgres J'ai compris que je devrais VACUUM
_ pg_largeobject
après le déshabillage des objets.
Je ne suis pas sûr que d'entre eux sont l'ensemble minimum requis ou le temps nécessaire à leur exécution, mais certains d'entre eux pourraient être utiles, et aucun ne devrait causer de dommages: j'ai couru VACUUM ANALYZE VERBOSE pg_largeobject_metadata; VACUUM ANALYZE VERBOSE pg_largeobject; VACUUM FULL ANALYZE VERBOSE pg_largeobject_metadata; VACUUM FULL ANALYZE VERBOSE pg_largeobject;
plusieurs fois pendant que le micro-instance dissociant les objets (prenait un très grand temps, hélas), d'abord lorsque environ 1/4 d'objets déjà gagné, et un peu de stockage a été remis au système d'exploitation, deuxièmement, lorsque environ 1/3 d'objets déjà gagnés, et certains Un autre petit stockage a été renvoyé au système d'exploitation, troisièmement, lorsque environ 3/5 objets ont déjà été non liés, l'instance a subi un budget de stockage massif au système d'exploitation:
Ce renvoi de stockage massif était ce que je cherchais. Après avoir exécuté la requête pour les plus grandes tables trouvées à la page d'accueil officielle , avec moins de 3/4 d'objets total non liés, la table d'objets a été réduite à moins de 3Gib, loin de la valeur initiale et encore plus gonflée 20Gib .
[~ # ~] NOTE [~ # ~ # ~] Automatiser rapideVACUUM ANALYZE
itération sur des tables a les mêmes effets à long terme (de nettoyer la table à partir d'utiliser un stockage en excès de disque) comme exécutant un seul VACUUM FULL
, mais sans acquérir une serrure exclusive.
Tous les crédits à https://dba.stackexchange.com/a/174664/83878 Mais je pourrais simplifier la solution dans mon cas. Il n'y a qu'un seul schéma dans mon cas. Aussi la première déclaration search_path = pg_catalog
ne fonctionne pas dans mon cas car je n'ai aucune autorisation pour modifier le catalogue du système. Je pourrais également simplifier considérablement la procédure de démontrage avec une seule instruction.
vacuum_lo_removeme
:Créer une table si ce n'est pas vide Vacuum_lo_Removeme comme sélectionnez OID comme LO de pg_catalog.pg_largeObject_metadata
Remarque: Ceci est créé dans le schéma par défaut maintenant.
SELECT c.relname as tablename, a.attname as columnname FROM pg_class c, pg_attribute a, pg_namespace s, pg_type t WHERE a.attnum > 0 AND NOT a.attisdropped AND a.attrelid = c.oid AND a.atttypid = t.oid AND c.relnamespace = s.oid AND t.typname in ('oid', 'lo') AND c.relkind in ('r','m') AND s.nspname !~ '^pg_' AND relname != 'vacuum_lo_removeme'
DELETE FROM vacuum_lo_removeme WHERE lo IN (SELECT ${columnname} FROM ${tablename}
Exemple: J'utilise Java ici (rs
est le résultat de l'instruction précédente):
while (rs.next()) {
String tablename = rs.getString("tablename");
String columnname = rs.getString("columnname");
// remove OID which must be kept
String removeUsedOids = String.format("DELETE FROM vacuum_lo_removeme WHERE lo IN (SELECT \"%s\" FROM \"%s\")",
columnname, tablename);
stmt = connection.createStatement();
stmt.executeUpdate(removeUsedOids);
}
SELECT count(lo_unlink(lo)) from vacuum_lo_removeme where lo in (select loid from pg_catalog.pg_largeobject)
DROP table vacuum_lo_removeme