ORA-01555 : Snapshot too old, la lecture qui échoue
Oracle n'a pas pu reconstituer l'image des données telle qu'elle était au début de votre lecture : une version dont il avait besoin, dans les informations d'annulation (l'undo), a été réutilisée entre-temps. La base n'est pas en panne et rien n'est plein au sens habituel. La cause la plus fréquente est une lecture plus longue que la rétention d'undo du moment ; ce n'est pas la seule, et cette page les distingue. Première chose à regarder : la durée de la requête qui a échoué, dans le journal d'alertes, face à la rétention réellement appliquée. Le premier contrôle est dans Vérifier.
Ce que vous voyez
En anglais
ORA-01555: snapshot too old: rollback segment number <n> with name "<nom>" too small
En français
ORA-01555: clichés trop vieux : rollback segment no <n>, nommé "<nom>", trop petit
Le message arrive dans la session qui lisait : un rapport, un export, un traitement de nuit, une requête lancée depuis un outil de requêtage. Le numéro et le nom entre guillemets désignent le segment d'undo dont Oracle avait besoin. Un numéro absent et un nom vide (with name "") sont la forme que la documentation montre pour une lecture de colonnes LOB : ce cas se règle ailleurs que dans le tablespace d'undo (voir plus bas).
Le journal d'alertes de la base porte au même moment une ligne de la forme ORA-01555 caused by SQL statement below (SQL ID: ..., Query Duration=... sec, SCN: ...), suivie du texte de la requête (format documenté sur le site du constructeur). C'est la première pièce à lire : elle nomme la requête qui a échoué et dit combien de temps elle a duré, donc combien de rétention il aurait fallu.
Ce que ça veut dire
Oracle garantit à toute lecture une image cohérente des données au moment où elle commence, même si d'autres sessions modifient les mêmes lignes pendant qu'elle tourne. Pour reconstituer cette image, il relit les anciennes valeurs dans les segments d'undo, où chaque modification laisse de quoi être annulée. Ces segments sont recyclés : une fois la transaction validée, ses informations d'annulation redeviennent réutilisables au bout d'un délai, la rétention. Si votre lecture dure plus longtemps que ce délai et qu'un bloc dont elle a besoin a été réécrit, Oracle ne peut plus fabriquer l'image cohérente. Il abandonne la lecture avec ce code plutôt que de rendre un résultat faux. C'est le sens de la phrase du constructeur : les enregistrements d'annulation dont un lecteur avait besoin ont été écrasés par d'autres écrivains (page d'erreur officielle).
Trois précisions :
- Ce n'est pas un tablespace plein. La base continue d'écrire ; seule la lecture longue échoue. L'erreur voisine ORA-30036 est l'inverse : là, ce sont les écritures qui manquent d'undo.
- La rétention est un minimum « au mieux », pas une garantie. Le paramètre
UNDO_RETENTIONfixe la durée minimale de conservation, et il n'est honoré que si le tablespace d'undo a l'espace pour cela (référence du paramètre). Avec un tablespace en extension automatique, la base l'honore en agrandissant le fichier, jusqu'à la taille maximale ; avec un tablespace de taille fixe, elle ajuste la rétention à ce que la taille permet, en visant 70 % de l'espace (Managing Undo). Au-delà de ce que l'espace porte, monter le paramètre ne change rien : c'est la taille qu'il faut regarder. - La garantie existe, et elle a un prix.
RETENTION GUARANTEEinterdit de recycler l'undo non expiré, même s'il faut pour cela faire échouer des écritures. La documentation dit de l'utiliser avec précaution.
Sur le terrain
La séquence typique : un traitement de nuit ou un rapport de fin de mois tourne depuis une ou deux heures, il échoue vers trois heures du matin, et le matin tout va bien. La même requête, relancée en journée, passe. Rien n'est plein, aucune alerte de capacité. C'est ce qui trompe : on cherche un incident, il n'y en a pas. Il y a une lecture plus longue que la rétention du moment, pendant une fenêtre où d'autres traitements écrivaient beaucoup.
Deux autres cas, souvent pris pour le premier :
- La session se prive elle-même de son undo. Un programme ouvre un curseur sur une table, la modifie ligne à ligne et valide à chaque ligne. Chaque validation libère l'undo de sa propre transaction, y compris ce dont son propre curseur aura besoin pour rester cohérent. Résultat : ORA-01555 sur une base presque vide, sans aucune autre session. Augmenter la rétention ne corrige pas le programme ; c'est le commit qu'il faut sortir de la boucle (le fil de référence sur le site du constructeur).
- Le numéro est absent et le nom est vide. C'est la forme du message que le constructeur cite pour un export de table à colonnes LOB, en renvoyant à sa note de support qui distingue ce cas d'un réglage
PCTVERSIONouRETENTIONtrop bas (chapitre Data Pump). Les anciennes versions d'un LOB ne vivent pas dans le tablespace d'undo mais dans le segment du LOB lui-même, gouvernées par son paramètre de stockageRETENTION(ouPCTVERSIONpour les LOB d'ancien format) ; sans elles, la lecture à un instant antérieur échoue avec ce même code (documentation des colonnes LOB). RéglerUNDO_RETENTIONn'y change rien.
Un cas plus rare, sur une base calme : après un très gros chargement, des blocs gardent un temps la trace de la transaction qui les a modifiés ; une lecture ultérieure doit consulter l'en-tête du segment d'undo pour savoir si cette transaction était validée, et si cet en-tête a été réutilisé entre-temps, c'est encore ORA-01555, sans aucune écriture concurrente. Le mécanisme, dit nettoyage différé des blocs, est expliqué sur le site du constructeur (AskTom).
Vérifier
Le premier contrôle ci-dessous est celui de Deynao pour ce code, en lecture seule, écrit ici avec l'ordre des lignes rendu explicite. Il demande SELECT sur V_$UNDOSTAT et V_$PARAMETER, accordés directement, sans rôle : c'est exactement ce que fait le script de privilèges de Deynao. Exécuté sur une base 21c ; les colonnes lues sont documentées dès la version 11.2 (référence 11.2), la plus ancienne que Deynao supervise.
-- 1. La tranche de dix minutes la plus récente (si elle date de moins d'une heure) :
-- rétention undo réellement appliquée, plus longue requête, occurrences de l'erreur
SELECT *
FROM (
SELECT begin_time,
end_time,
ROUND(tuned_undoretention / 60, 2) AS tuned_retention_minutes,
ROUND(maxquerylen / 60, 2) AS longest_query_minutes,
maxqueryid,
ssolderrcnt AS ora_01555_count,
txncount AS total_transactions,
maxconcurrency AS max_concurrent_txns
FROM v$undostat
WHERE end_time >= SYSDATE - 1/24
ORDER BY end_time DESC
)
WHERE ROWNUM = 1;
Comment le lire. V$UNDOSTAT garde une ligne par tranche de dix minutes, sur quatre jours (référence de la vue). ora_01555_count compte les occurrences de l'erreur dans la tranche. longest_query_minutes est la durée de la plus longue requête exécutée pendant la tranche, mesurée de l'ouverture du curseur à sa dernière lecture, et maxqueryid son identifiant : ce n'est pas forcément la requête qui a échoué, que seul le journal d'alertes nomme. tuned_retention_minutes est la rétention que la base appliquait réellement à ce moment. Quand une lecture dure plus longtemps que cette rétention, l'erreur devient possible à tout moment : il suffit qu'un bloc dont elle a besoin ait été recyclé. L'inverse n'est pas une garantie. Un résultat vide signifie qu'aucune tranche n'a été enregistrée dans la dernière heure, par exemple juste après un démarrage ; une erreur ORA-00942 signifie que ce compte n'a pas accès à la vue.
Pour retrouver les tranches où l'erreur s'est produite :
-- 2. Les tranches touchées sur les quatre derniers jours, et la plus longue requête de chacune
-- (pas forcément celle qui a échoué : le journal d'alertes la nomme)
SELECT begin_time,
ssolderrcnt AS ora_01555_count,
ROUND(maxquerylen / 60, 2) AS longest_query_minutes,
maxqueryid
FROM v$undostat
WHERE ssolderrcnt > 0
ORDER BY begin_time DESC;
Pour savoir si la base manquait d'espace d'undo au moment des erreurs, plutôt que de ne regarder que la durée des requêtes :
-- 3. Pression sur l'undo dans les dernières vingt-quatre heures : tranches où la base a repris
-- de l'undo non expiré, a manqué d'espace, ou a produit l'erreur
SELECT begin_time,
unxpstealcnt AS reprises_undo_non_expire,
unxpblkrelcnt AS blocs_non_expires_liberes,
nospaceerrcnt AS manques_espace,
ssolderrcnt AS ora_01555_count
FROM v$undostat
WHERE end_time >= SYSDATE - 1
AND (unxpstealcnt > 0 OR unxpblkrelcnt > 0 OR nospaceerrcnt > 0 OR ssolderrcnt > 0)
ORDER BY begin_time DESC;
unxpstealcnt compte les tentatives de reprendre de l'undo non expiré à d'autres transactions, unxpblkrelcnt les blocs non expirés effectivement libérés pour d'autres transactions (même référence de la vue). Des valeurs non nulles dans les tranches où l'erreur apparaît disent que la base manquait d'espace d'undo et recyclait ce que des lectures pouvaient encore réclamer : la réponse est alors de l'espace, pas seulement de la rétention. Des compteurs à zéro avec l'erreur présente désignent plutôt une lecture plus longue que la rétention ajustée, ou l'un des cas particuliers décrits plus haut.
Pour l'état du tablespace d'undo à la dernière tranche (blocs de 8 ko : adapter si db_block_size diffère) :
-- 4. État du tablespace d'undo à la dernière tranche : actif, non expiré, expiré
SELECT *
FROM (
SELECT ROUND(activeblks * 8192 / 1024 / 1024, 2) AS active_mb,
ROUND(unexpiredblks * 8192 / 1024 / 1024, 2) AS unexpired_mb,
ROUND(expiredblks * 8192 / 1024 / 1024, 2) AS expired_mb,
end_time
FROM v$undostat
ORDER BY end_time DESC
)
WHERE ROWNUM <= 1;
Si l'espace expiré est proche de zéro, tout ce qui reste est actif ou non expiré : sur un tablespace de taille fixe, ou arrivé à sa taille maximale, c'est la taille qui borne la rétention, et monter le paramètre sans donner d'espace ne suffira pas. Les paramètres en vigueur, pour finir :
-- 5. Les paramètres d'undo en vigueur
SELECT name, value
FROM v$parameter
WHERE name IN ('undo_retention', 'undo_tablespace', 'undo_management');
Agir
Dans l'ordre, et chaque étape peut suffire :
- Lire la durée de la requête qui a échoué dans le journal d'alertes (
Query Duration), et la comparer àTUNED_UNDORETENTION.MAXQUERYLENne donne que la plus longue requête de la tranche, pas forcément celle-là. Tant que la lecture dure plus que la rétention disponible, aucune autre action ne tient. - Raccourcir ou déplacer la lecture. Un rapport de deux heures lancé pendant la fenêtre d'écriture la plus dense a plus de chances d'échouer que le même rapport à un autre moment. Optimiser la requête vaut souvent mieux qu'agrandir l'undo.
- Donner de la rétention, et l'espace qui va avec. Monter
UNDO_RETENTIONau-dessus de la durée de la plus longue lecture. Exemple à adapter, jamais à copier tel quel :
ALTER SYSTEM SET undo_retention = 3600 SCOPE = BOTH;
Le paramètre n'est honoré que si l'espace le permet : si le contrôle 3 montre des reprises d'undo non expiré, ou si le tablespace est de taille fixe et déjà occupé par ce que les lectures réclament, agrandir le fichier d'undo, ou le passer en extension automatique avec une taille maximale, avant de compter sur le paramètre. Le bon réglage dépend de la charge, de la configuration et de l'espace réellement disponible, pas d'une règle unique.
- Sortir le commit de la boucle si le programme lit et valide la même table ligne à ligne. Aucun réglage de la base ne corrige ce cas.
- Pour un LOB, régler la rétention du LOB (
RETENTIONdu stockage SecureFiles,PCTVERSIONpour l'ancien format), pas celle de l'undo.
Votre DBA valide, adapte et décide : une rétention plus longue demande plus d'espace d'undo, et un undo saturé fait échouer les écritures (ORA-30036), ce qui est pire qu'un rapport à relancer.
Prévenir
Deux mesures tiennent l'essentiel :
- Une rétention supérieure à la plus longue lecture connue, avec un tablespace d'undo capable de la porter. La documentation fournit un Undo Advisor, alimenté par les statistiques de charge, pour dimensionner le tablespace à partir de la durée attendue de la plus longue requête (Managing Undo).
- Une surveillance de
SSOLDERRCNTet des compteurs de pression : la vue les compte par tranche de dix minutes sur quatre jours, sans rien installer. La base émet aussi une alerte native sur les requêtes longues qui provoquent ce code, au plus une fois par vingt-quatre heures (même page de la documentation).
Ce qu'on ne fait pas : activer RETENTION GUARANTEE pour qu'un rapport passe. On échange alors un rapport à relancer contre des écritures qui échouent.
Ce que Deynao en fait
Deynao lit le journal d'alertes de chaque base supervisée et y reconnaît ce code. Il le classe en Vigilance : la base répond et écrit, seule une lecture a échoué. Cette classification est celle du code dans Deynao ; l'impact réel dépend du traitement interrompu, une requête exploratoire ou une clôture de fin de mois, et c'est à l'exploitant de l'apprécier. La fiche affichée explique le mécanisme et donne le contrôle à lancer, avec le privilège qu'il demande. Ce code n'entre pas dans la règle « Alertes Oracle critiques », réservée aux codes critiques ; il apparaît dans le journal expliqué du produit, à sa sévérité.
En amont, le domaine Journalisation lit la rétention d'undo à chaque collecte. Sous 900 secondes, Deynao émet la recommandation « Augmenter la rétention UNDO », avec la cible de 1 800 secondes et le contrôle à lancer ; entre 900 et 1 800 secondes, le score du domaine est simplement pénalisé. Ces seuils sont ceux des règles actuelles de Deynao ; ils ne remplacent pas le dimensionnement de l'undo selon la charge et les lectures propres à chaque base. Deynao lit aussi l'occupation du tablespace d'undo en tenant compte de sa taille maximale d'extension, pas seulement de la taille actuelle du fichier : un fichier de 3 Go occupé à 84 % qui peut grandir jusqu'à 32 Go n'est pas un tablespace plein. Les règles d'alerte du produit et leurs seuils sont publiés : règles d'alerte et temporisations.
Erreurs liées
- ORA-30036 : l'undo manque pour les écritures, l'inverse de celui-ci
- ORA-01562 : échec d'extension d'un segment d'annulation
Sources
- ORA-01555, portail d'erreurs du constructeur
- Managing Undo, guide d'administration 19c : rétention au mieux, garantie, ajustement automatique, Undo Advisor, alerte native
- UNDO_RETENTION, référence 21c : le paramètre n'est honoré que si l'espace le permet
- V$UNDOSTAT, référence 19c : les colonnes lues par les contrôles
- V$UNDOSTAT, référence 11.2 : les mêmes colonnes dans la version la plus ancienne supervisée
- Colonnes LOB et conservation des anciennes versions, guide 21c
- ORA-01555 à numéro et nom vides sur un export de colonnes LOB, chapitre Data Pump
- La ligne « ORA-01555 caused by SQL statement below » du journal d'alertes, AskTom
- Le fil de référence sur ORA-01555 et le commit en boucle, AskTom
- Nettoyage différé des blocs, AskTom