2016-03-16 2 views
1

J'ai rencontré un problème lors de l'interrogation de certaines de mes tables récemment. Lorsque j'essaie de sélectionner des données, j'obtiens une ERREUR indiquant: ERREUR: taille de demande d'allocation de mémoire invalide 4294967293. Cela indique généralement une corruption de données. Il y a une technique gentille et précise de comment supprimer des rangées corrompues décrites ici: https://confluence.atlassian.com/jirakb/invalid-memory-alloc-request-size-440107132.html
Mais, puisque j'ai beaucoup de tables corrompues, cette méthode est trop lente. Donc, j'ai trouvé une bonne fonction qui renvoie le dernier ctid réussi ici: http://blog.dob.sk/2012/05/19/fixing-pg_dump-invalid-memory-alloc-request-size/Correction d'une demande d'allocation de mémoire invalide à PostgreSQL 9.2.9

Vous cherchez une ligne corrompue est un peu plus rapide lors de son utilisation, mais pas assez rapide. Je légèrement modifié pour stocker tous « dernier ctid réussi » dans une autre table et il ressemble maintenant à ceci:

CREATE OR REPLACE FUNCTION 
find_bad_row(tableName TEXT) 
RETURNS void 
as $find_bad_row$ 
DECLARE 
result tid; 
curs REFCURSOR; 
row1 RECORD; 
row2 RECORD; 
tabName TEXT; 
count BIGINT := 0; 
BEGIN 
DROP TABLE IF EXISTS bad_rows_tbl; 
CREATE TABLE bad_rows_tbl (id varchar(255), offs BIGINT); 
SELECT reverse(split_part(reverse($1), '.', 1)) INTO tabName; 

OPEN curs FOR EXECUTE 'SELECT ctid FROM ' || tableName; 

count := 1; 

FETCH curs INTO row1; 

WHILE row1.ctid IS NOT NULL LOOP 
    BEGIN 
    result = row1.ctid; 

    count := count + 1; 
    FETCH curs INTO row1; 

    EXECUTE 'SELECT (each(hstore(' || tabName || '))).* FROM ' 
    || tableName || ' WHERE ctid = $1' INTO row2 
    USING row1.ctid; 

    IF count % 100000 = 0 THEN 
    RAISE NOTICE 'rows processed: %', count; 
    END IF; 
    EXCEPTION 
    WHEN SQLSTATE 'XX000' THEN 
     RAISE NOTICE 'LAST CTID: %', result; 
     EXECUTE 'INSERT INTO bad_rows_tbl VALUES(' || result || ',' || count || ')'; 
    END; 

END LOOP; 

CLOSE curs; 

END 
$find_bad_row$ 
LANGUAGE plpgsql; 

Je suis tout à fait nouveau pour plpgsql, donc je suis coincé avec la question suivante: comment interroger pas pré-infructueux ctid, mais l'échec exact (ou calculer le suivant de pré-échoué) afin que je puisse l'insérer dans bad_rows_tbl et l'utiliser comme argument pour une déclaration DELETE plus loin?

Espoir pour un peu d'aide ...

UPD: une fonction que je fini par

CREATE OR REPLACE FUNCTION 
find_bad_row(tableName TEXT) 
RETURNS tid[] 
as $find_bad_row$ 
DECLARE 
result tid; 
curs REFCURSOR; 
row1 RECORD; 
row2 RECORD; 
tabName TEXT; 
youNeedMe BOOLEAN = false; 
count BIGINT := 0; 
arrIter BIGINT := 0; 
arr tid[]; 
BEGIN 
CREATE TABLE bad_rows_tbl (id varchar(255), offs BIGINT); 
SELECT reverse(split_part(reverse($1), '.', 1)) INTO tabName; 

OPEN curs FOR EXECUTE 'SELECT ctid FROM ' || tableName; 

count := 1; 

FETCH curs INTO row1; 

WHILE row1.ctid IS NOT NULL LOOP 
    BEGIN 
    result = row1.ctid; 
    count := count + 1; 

    IF youNeedMe THEN 
     arr[arrIter] = result; 
     arrIter := arrIter + 1;  
     RAISE NOTICE 'ADDING CTID: %', result; 
     youNeedMe = FALSE; 
    END IF; 

    FETCH curs INTO row1; 

    EXECUTE 'SELECT (each(hstore(' || tabName || '))).* FROM ' 
    || tableName || ' WHERE ctid = $1' INTO row2 
    USING row1.ctid; 

    IF count % 100000 = 0 THEN 
     RAISE NOTICE 'rows processed: %', count; 
    END IF; 
    EXCEPTION 
    WHEN SQLSTATE 'XX000' THEN 
     RAISE NOTICE 'LAST GOOD CTID: %', result; 
     youNeedMe = TRUE; 
    END; 

END LOOP; 

CLOSE curs; 
RETURN arr; 
END 
$find_bad_row$ 
LANGUAGE plpgsql; 
+1

Comment es-tu dans la situation en premier lieu? Est-ce que la restauration à un point dans le temps (barman, etc) après la restauration à partir de la sauvegarde n'est pas une option? –

+1

Malheureusement, étant donné que la commande 'archive_mode' dans postgresql.conf est commentée, je suppose que la récupération à un point dans le temps n'est pas disponible. C'est pourquoi j'essaye de réparer le contenu. –

+1

J'ai modifié une requête et ajouté une fonction que j'ai terminée. Il renvoie un tableau de TID de lignes corrompues. Il peut être utilisé à une clause WHERE d'une instruction DELETE. J'ai déjà supprimé plusieurs lignes brisées en l'utilisant. –

Répondre

1

Ceci est complémentaire à la fonction donnée dans la question et les réponses des prochaines étapes après la db est dumpable.

Vos prochaines étapes devraient être:

  1. dumpall et restaurer sur un système physique différent. La raison étant à ce stade, nous ne savons pas ce qui a causé cela et les chances ne sont pas si mauvaises que cela pourrait être le matériel.

  2. Vous devez retirer l'ancien système et lancer des diagnostics matériels, à la recherche de problèmes. Vous voulez vraiment savoir ce qui s'est passé afin de ne pas le retrouver. D'un intérêt particulier:

    • Double contrôle ECC RAM et les journaux Carto
    • Regardez tous les tableaux RAID et leurs sauvegardes batterie
    • CPU et PSU
    • Si c'était moi je regarde aussi les variables environnementales telles comme AC dans et la température du centre de données.
  3. Passez en revue votre stratégie de sauvegarde. En particulier, regardez PITR (et l'utilitaire associé pgbarman). Assurez-vous de pouvoir vous remettre d'une situation similaire dans le futur si vous y êtes confronté.

La corruption de données ne se produit pas simplement. Dans de rares cas, il peut être causé par des bogues dans PostgreSQL, mais dans la plupart des cas, cela est dû à votre matériel ou à cause du code personnalisé que vous avez exécuté dans le back-end. Réduire la cause et assurer la recouvrabilité sont critiques à l'avenir.

En supposant que vous n'êtes pas en cours d'exécution du code C personnalisé dans votre base de données, plus probablement votre corruption de données est due à quelque chose sur le matériel