Cet article est la traduction d’un article de Jonathan Lewis publié sur son blog. L’article original en anglais se trouve ici.
Le ROWID identifie un enregistrement d’une table dans la base de données, à partir de l’adresse physique du bloc et du numéro d’enregistrement dans le bloc. Il est utilisé principalement dans les indexes pour pointer sur l’enregistrement de la table, et dans les tables pour les pointeurs des chained rows. C’est le moyen le plus direct car il permet d’aller directement sur le bloc qui contient l’enregistrement.
Jusqu’à Oracle 7, l’adresse physique d’un bloc était constitué du numéro du fichier de la base (absolute file_number, AFN ou FILE_ID ou FILE#) et du numéro de bloc relatif au fichier (block_number ou BLOCK_ID ou BLOCK#). L’ensemble est appelé DBA: Data Block Address. Le ROWID utilise cela pour identifier le bloc, et y ajoute le numéro d’enregistrement dans le bloc (ROW_NUMBER ou ROW#).
A partir d’Oracle 8, l’identification des fichiers est relative au tablespace. Cela permet de supporter plus de fichiers dans une base, et de rendre les tablespaces plus indépendants. On parle alors de numéro de fichier relatif à la tablespace (relative file_number, RFN ou RELATIVE_FNO ou RFILE#). Avec le numéro de bloc relatif au fichier, l’ensemble constitue le RDBA: Relative Data Block Address. Pour les bigfile tablespaces, ne comportant qu’un seul fichier, il s’agit seulement du block#.
Pour trouver un bloc dans la base, il est donc nécessaire de connaître aussi le tablespace. Plutôt que d’ajouter le numéro de tablespace dans le ROWID, c’est le DATA_OBJECT_ID (DATAOBJ# ou OBJD ou OBJ ou OBJECT_NUMBER) qui est utilisé. Il s’agit de l’identifiant de l’objet physique, c’est à dire du segment, contrairement à l’OBJECT_ID (OBJ# ou OBJN) qui est l’identifiant de l’objet logique. Le DATA_OBJECT_ID permet d’identifier le tablespace grâce au dictionnaire, puisque un segment se trouve dans un et un seul tablespace.
Ainsi, le ROWID ne comprends que des informations physiques pour identifier le bloc (segment, datafile, block). C’est ce qui rend optimal les tablespaces transportables ainsi que l’échange de partitions, car ils n’ont pas à modifier le contenu des blocs mais seulement les méta-données du dictionnaire.L’ancien ROWID est appelé le Restricted ROWID, il est affiché sous la forme block#.row#.file# et celui qui inclut le data_objet_id est appelé Extended ROWID, il est affiché encodé (6 caractères pour dataobj#, 9 caractères pour file#/block#, 3 caractères pour row#).
Cet article de Jonathan Lewis explique la taille nécessaire au stockage du ROWID dans différents cas, ce qui peut être utile pour estimer la taille d’un index par exemple.
Dans une récente discussion sur le blog un article de Charles Hooper , j’ai fait un commentaire disant qu’il est difficile d’être précis et non-ambigu lorsqu’on estime l’espace nécessaire au stockage du ROWID. Je vais donc essayer d’énumérer tous les cas possible que l’on peut rencontrer. Franchement, je ne suis pas sûr d’être exhaustif dès le premier jet.
Alors, quelle place prend un ROWID ?
Disons que je suis un enregistrement d’une table normale (heap table – nous n’allons pas prendre en compte le cas des IOT, tables organisées comme des indexes, parce que leurs enregistrements sont adressés par la clé primaire et non par le ROWID). Alors je me trouve dans un bloc de données, et ce bloc enregistre dans son entête le data object id, le relative file number, et le block id. C’est la première composante de mon adresse. La seule autre ichose qui m’est nécessaire de connaître est le numéro de l’entrée que j’occupe dans la liste des enregistrements du bloc(row directory) Et comme le row directory peut contenir jusqu’à 4096 entrées, ce numéro prends 2 octets. Donc – de mon point de vue – la taille de mon rowid est de deux octets seulement.
Mais qu’en est-il des autres personnes qui ont besoin de mon adresse ? Combien d’espace doivent-ils allouer pour stocker mon ROWID ? Techniquement, ils ont besoin du data object id (pour leur permettre de trouver le numéro du tablespace), du numéro de fichier dans le tablespace (relative file number), du numéro de bloc dans le fichier (block number), et du numéro de l’entrée dans le row directory (row number). Il s’agit de 4 octets pour le data object id, 2 octets pour le row number. Le numéro de bloc et le numéro de fichier sont stockés ensembles sur 4 octets.
Il y a deux formats pour cette dernière partie:
- Pour les ‘bigfile tablespaces’ qui n’ont toujours qu’un seul fichier par tablespace, les 4 octets sont tous utilisés pour le numéro de bloc.
- Pour les tablespaces traditionnels, 10 bits sont réservés pour le numéro relatif de fichier ce qui permet jusqu’à 1022 fichiers par tablespace, et le reste est pour le numéro de bloc dans le fichier.
Donc dans le cas général, un rowid prends dix octets.
Mais différentes parties du code peuvent se comporter différemment, plus ou mois efficaces pour la gestion de l’espace.
Si mon rowid est stocké dans une autre table, dans une colonne de type ROWID (ce qui est très souvent une mauvaise idée), ou s’il est stocké temporairement dans une variable d’un programme de type ROWID alors la table ou la variable va devoir stocker les dix octets.
Pour l’utilisateur final, le rowid doit être affiché sous forme lisible de sorte que le rowid de dix octets est transformé en une chaîne de 18 caractères.
Si mon rowid doit être stockée dans un index de ma table, alors il y a plusieurs cas.
Si la table n’est pas partitionnée, alors le code interne peut déduire mon data object id du fait que l’index ne peut pointer que sur le segment de ma table. Du coup, l’index n’a pas à stocker les 4 octets du data object id.
Dans ce cas, si c’est un index unique alors le rowid est stocké comme une valeur de taille fixe, associée à la clé primaire, et il prends 6 octets. Si l’index n’est pas unique, alors le rowid est stocké comme une colonne supplémentaire à la fin de l’index. Toute valeur de colonne est précédée par un octet qui donne sa taille (length byte). Ce qui veut dire que la taille nécessaire à son stockage est de 7 octets.
Si ma table est une table partitionnée, alors la même logique de déduction du data object id peut toujours s’appliquer pour un index local: Par définition, la partition n
de l’index local pointe obligatoirement vers la partition n
de la table. Donc de la même manière, suivant que l’index est unique ou non, le stockage est toujours de 6 ou 7 octets.
Mais si c’est un index global ou global/partitionné, alors n’importe quelle entrée de l’index peut pointer sur n’importe quelle partition de la table. Dans ce cas, l’entrée d’index doit inclure les 4 octets du data object id de la partition dans laquelle se trouve l’enregistrement. Et dans ce cas, suivant que l’index est unique ou non, la taille du rowid est de 10 ou 11 octets.
Je pense cela couvre à peu près tous les cas. Mais si vous voulez vous creuser un peu la tête, vous pouvez réfléchir à ce qu’il se passe dans un index cluster et si cela introduit un type de rowid différent.