Diferença e significado das colunas CDB_ID e PDB_ID em um ambiente MULTI-TENANT
Recentemente, um colega num dos grupos de discussão que frequento estava com dúvidas sobre porque existem colunas CON_ID e PDB_ID nas views do sistema, quando e como usar cada uma delas. O objetivo deste artigo é esclarecer os conceitos envolvidos e tentar indicar quando e como / para que as usar.
Quando temos um ambiente multi-tenant, nós temos um database CDB (que seria o ‘database de controle’, database “MASTER” se vc quiser fazer um paralelo com SQL SERVER, ie, um database em princípio dedicado a gerenciar os outros databases). E temos N PDBs (PDBs= Pluggable Databases, são os databases a conter os dados dos usuários finais, databases esses gerenciados/controlados pelo database CDB/MASTER, e que inclusive tem esse nome de PLUGGABLE porque podem ser desplugados de um CDB e plugados em outro)….
Então, a PRIMEIRA coisa que você TEM que saber é que pra poder obter detalhes sobre PDBs em princípio você ** TEM ** que estar conectado no banco CDB : é ELE que conhece, é ELE quem ‘controla’ os PDBs todos…. Veja só:
Estou conectado no database CDB:
SYSTEM@xe>select cdb, name from v$database;
CDB NAME
--- ----
YES XE
1 linha selecionada.
SYSTEM@xe>
Consigo ver info sobre TODOS os PDBs controlados/gerenciados pelo CDB :
SYSTEM@xe>select PDB_ID, PDB_NAME FROM CDB_PDBS ORDER BY 1;
PDB_ID PDB_NAME
---------- -----------
2 PDB$SEED
3 XEPDB1
2 linhas selecionadas.
SYSTEM@xe>
(Esse PDB$SEED é um PDB “interno”, usado como um ‘modelo’, um ‘template’ quando você vai criar um database PDB, desconsidere-o de momento)…
Muito bem. Já se estiver conectado num PDB, ** óbvio ** que ele não tem “permissão” pra enxergar os outros PDBs :
SYSTEM@xepdb1>select PDB_ID, PDB_NAME FROM CDB_PDBS;
PDB_ID PDB_NAME
---------- --------
3 XEPDB1
1 linha selecionada.
SYSTEM@xepdb1>
Esse é o primeiro ponto que eu TENHO que indicar, se você for administrar um ambiente MULTI-TENANT tenha CERTEZA que teus scripts gerais que desejam consultar dados gerais estão rodando no banco CDB , okdoc ??
Muito bem, agora vamos falar de CONTAINERS. Pense no CONTAINER como uma área Lógica, aonde são mantidos dados sobre os databases – e o próprio database CDB , sendo um database, tem um Container a que se refere.E a cada vez que um PDB é criado, um Container relacionado com ele é criado também:
SYSTEM@xe>select con_id, name from v$containers order by 1;
CON_ID NAME
---------- -----------
1 CDB$ROOT
2 PDB$SEED
3 XEPDB1
3 linhas selecionadas.
SYSTEM@xe>
Vamos checar os PDBs controlados por este banco CDB:
SYSTEM@xe>select PDB_ID, PDB_NAME, CON_ID FROM CDB_PDBS order by 1;
PDB_ID PDB_NAME CON_ID
---------- ----------- ----------
2 PDB$SEED 2
3 XEPDB1 3
2 linhas selecionadas.
SYSTEM@xe>
Se eu criar um novo PDB, automagicamente um novo CONTAINER será registrado:
SYSTEM@xe>create pluggable database "BOOKSTORE"
2 admin user "BOOKSTOREADMIN"
3 identified by "BOOKSTOREADMIN"
4 file_name_convert = ('d:\app\oracle\product\XE\18.0.0\oradata\XE\pdbseed\', 'd:\app\oracle\product\XE\18.0.0\oradata\XE\bookstore\');
Banco de dados plugável criado.
SYSTEM@xe>select PDB_ID, PDB_NAME FROM CDB_PDBS;
SYSTEM@xe>select con_id, name from v$containers order by 1;
CON_ID NAME
---------- -----------
1 CDB$ROOT
2 PDB$SEED
3 XEPDB1
4 BOOKSTORE
4 linhas selecionadas.
E taí o novo PDB também registrado:
SYSTEM@xe>select PDB_ID, PDB_NAME, CON_ID FROM CDB_PDBS order by 1;
PDB_ID PDB_NAME CON_ID
---------- ----------- ----------
2 PDB$SEED 2
3 XEPDB1 3
4 BOOKSTORE 4
3 linhas selecionadas.
SYSTEM@xe>
Vou aproveitar e já ABRIR o novo PDB também:
SYS@xe>alter pluggable database "BOOKSTORE" open read write;
Banco de dados plugável alterado.
SYS@xe>
Muito bem. Voltando à lista de PDBs e CONTAINERS acima, veja que o CONTAINER 1 (o CDB$ROOT) não aparece na view que lista os PDBs porque NÂO se relaciona a um PDB mas sim ao database CDB….
Agora SIM, chegamos no ponto do artigo que é o porquê de algumas views do sistema conterem CON_ID e outras conterem PDB_ID. CON_ID vai ser preenchido para objetos que TANTO podem estar em databases CDB quanto em PDB. Por exemplo, tablespaces.
SYSTEM@xe>desc CDB_TABLESPACES
Nome Nulo? Tipo
----------------------------------- -------- -----------------
TABLESPACE_NAME NOT NULL VARCHAR2(30)
BLOCK_SIZE NOT NULL NUMBER
INITIAL_EXTENT NUMBER
NEXT_EXTENT NUMBER
MIN_EXTENTS NOT NULL NUMBER
MAX_EXTENTS NUMBER
MAX_SIZE NUMBER
PCT_INCREASE NUMBER
MIN_EXTLEN NUMBER
STATUS VARCHAR2(9)
CONTENTS VARCHAR2(21)
LOGGING VARCHAR2(9)
FORCE_LOGGING VARCHAR2(3)
EXTENT_MANAGEMENT VARCHAR2(10)
ALLOCATION_TYPE VARCHAR2(9)
PLUGGED_IN VARCHAR2(3)
SEGMENT_SPACE_MANAGEMENT VARCHAR2(6)
DEF_TAB_COMPRESSION VARCHAR2(8)
RETENTION VARCHAR2(11)
BIGFILE VARCHAR2(3)
PREDICATE_EVALUATION VARCHAR2(7)
ENCRYPTED VARCHAR2(3)
COMPRESS_FOR VARCHAR2(30)
DEF_INMEMORY VARCHAR2(8)
DEF_INMEMORY_PRIORITY VARCHAR2(8)
DEF_INMEMORY_DISTRIBUTE VARCHAR2(15)
DEF_INMEMORY_COMPRESSION VARCHAR2(17)
DEF_INMEMORY_DUPLICATE VARCHAR2(13)
SHARED VARCHAR2(13)
DEF_INDEX_COMPRESSION VARCHAR2(8)
INDEX_COMPRESS_FOR VARCHAR2(13)
DEF_CELLMEMORY VARCHAR2(14)
DEF_INMEMORY_SERVICE VARCHAR2(12)
DEF_INMEMORY_SERVICE_NAME VARCHAR2(1000)
LOST_WRITE_PROTECT VARCHAR2(7)
CHUNK_TABLESPACE VARCHAR2(1)
CON_ID NUMBER
SYSTEM@xe>
Em views que se referem à PDBs, vou ter o PDB_ID:
SYSTEM@xe>desc cdb_pdbs
Nome Nulo? Tipo
----------------------------------------- -------- --------------
PDB_ID NOT NULL NUMBER
PDB_NAME NOT NULL VARCHAR2(128)
DBID NOT NULL NUMBER
CON_UID NOT NULL NUMBER
GUID RAW(16)
STATUS VARCHAR2(10)
CREATION_SCN NUMBER
VSN NUMBER
LOGGING VARCHAR2(9)
FORCE_LOGGING VARCHAR2(39)
FORCE_NOLOGGING VARCHAR2(3)
APPLICATION_ROOT VARCHAR2(3)
APPLICATION_PDB VARCHAR2(3)
APPLICATION_SEED VARCHAR2(3)
APPLICATION_ROOT_CON_ID NUMBER
IS_PROXY_PDB VARCHAR2(3)
CON_ID NOT NULL NUMBER
UPGRADE_PRIORITY NUMBER
APPLICATION_CLONE VARCHAR2(3)
FOREIGN_CDB_DBID NUMBER
UNPLUG_SCN NUMBER
FOREIGN_PDB_ID NUMBER
CREATION_TIME NOT NULL DATE
REFRESH_MODE VARCHAR2(6)
REFRESH_INTERVAL NUMBER
TEMPLATE VARCHAR2(3)
LAST_REFRESH_SCN NUMBER
TENANT_ID VARCHAR2(255)
SNAPSHOT_MODE VARCHAR2(6)
SNAPSHOT_INTERVAL NUMBER
SYSTEM@xe>
Já que não tenho PDB_ID, tenho que usar CON_ID para consultar as tablespaces na view:
SYSTEM@xe>select distinct CON_ID from CDB_TABLESPACES;
CON_ID
----------
1
3
4
3 linhas selecionadas.
SYSTEM@xe>
Novamente, o Container 1 se refere a um database que não É PDB, então esse CON_ID = 1 não estará contido nas views que se referem à PDBs, como por exemplo a CDB_PDBS. Assim sendo, tenho que fazer um OUTER JOIN se eu quiser mostrar as tablespaces TODAS existentes no ambiente e (quando for uma tablespace referente à PDB) mostrar o nome do PDB:
SYSTEM@xe>select A.CON_ID, B.PDB_NAME, A.TABLESPACE_NAME from CDB_TABLESPACES A, CDB_PDBS B
2 WHERE A.CON_ID = B.PDB_ID (+)
3 order by 1,3;
CON_ID PDB_NAME TABLESPACE_NAME
---------- ----------- ------------------------------
1 SYSAUX
1 SYSTEM
1 TEMP
1 UNDOTBS1
1 USERS
3 XEPDB1 SYSAUX
3 XEPDB1 SYSTEM
3 XEPDB1 TEMP
3 XEPDB1 UNDOTBS1
3 XEPDB1 USERS
4 BOOKSTORE SYSAUX
4 BOOKSTORE SYSTEM
4 BOOKSTORE TEMP
4 BOOKSTORE UNDOTBS1
14 linhas selecionadas.
SYSTEM@xe>
Ou seja, a resposta final do artigo é: Nem todas as views do sistema ORACLE num banco CDB possuem PDB_ID porque nem todas se relacionam à PDBs, E caso haja a necessidade de joinear uma view com info de PDB contra uma sem (como foi o caso acima) pode se usar CON_ID…
Para finalizar, três observações:
1. Antes de mais nada, eu tenho que lembrar que a opção de MULTI-TENANT (ie, ter múltiplos databases ‘controlados’ por um database ‘mestre’ e com um só conjunto de processos principais atendendo a todos) foi introduzida na versão 12.1 do RDBMS Oracle, na forma mais simples,que foi a referenciada aqui no artigo. Na versão 12.2 em diante foi INTRODUZIDA a funcionalidade de Application Containers, que é um elemento lógico de controle ADICIONAL a mais para levar em conta.
Os interessados podem se aprofundar lendo este link e a série de artigos sobre Oracle Multi-tenant.
2. A numeração constante na coluna CON_ID ** não é ** aleatória/desprovida de significado. A documentação da Oracle já nos diz:
Each container data object contains a CON_ID column that identifies the container for each row returned.
E também:
Table 19-1 describes the meanings of the values in the CON_ID column.
Table 19-1 CON_ID Column in Container Data Objects
Value in CON_ID Column Description
0 The data pertains to the entire CDB
1 The data pertains to the CDB root
2 The data pertains to the PDB seed
3 – 4,098 The data pertains to a PDB, an application root, or an application PDB
Ou seja, CON_ID é o identificador DO CONTAINER ao qual o PDB está relacionado, os Containers 0, 1 e 2 são ‘reservados’, de uso interno, possuem significado diferenciado nas views do sistema…
3. Não está relacionado com o tema do Artigo, mas há o conceito de CURRENT CONTAINER – mudando o CONTAINER ao qual você está conectado, você passa a enxergar os dados como se estivesse conectado ao database relacionado com o novo CONTAINER:
SYSTEM@xe>alter session set CONTAINER=XEPDB1;
Sessão alterada.
Pronto. Passo a enxergar os dados como se tivesse conectado ao database XEPDB1, que se relaciona com o container XEPDB1:
SYSTEM@xe>select con_id, name from v$containers;
CON_ID NAME
---------- -----------
3 XEPDB1
1 linha selecionada.
SYSTEM@xe>select PDB_ID, PDB_NAME FROM CDB_PDBS ORDER BY 1;
PDB_ID PDB_NAME
---------- -----------
3 XEPDB1
1 linha selecionada.
SYSTEM@xe>
=> Obviamente, só pode fazer isso um usuário Autorizado/permissionado…
Abraços