アプリケーションPDBあれこれ

12cR2のころから機能としては存在するらしいアプリケーションPDB。

ただでさえややこしいマルチテナント構成をさらにややこしくする面倒なやつ。イメージとしてはPDBの下にさらに別のPDBをぶら下げる形になる。

実際に使っている人は一度も見たことがないが、Oracle Master Goldの資格試験の範囲なのでいろいろ雑多な動作確認をしてみる。

個人的な確認メモなので、細かい説明は省いている。詳細はマニュアルを参照のこと。

アプリケーション・コンテナおよびシードの作成と削除 (oracle.com)

アプリケーション・コンテナの管理 (oracle.com)

単純な作成例

アプリケーションコンテナ(アプリケーションルート)として「appcon1」というPDBを作り、

さらにその下にアプリケーションPDBとして「apppdb1」というPDBを作る。

アプリケーションコンテナ内でアプリケーションを定義して、適宜それをアプリケーションPDBに同期する。ここでいう「アプリケーション」とは、ユーザや表などのデータベース上のオブジェクト定義や、その表に入っているデータを指しており、アプリケーションを同期することでPDBに対して簡単にユーザや表定義の変更などを反映させることができる。

まずは、以下のようにアプリケーションコンテナを作成し、OPENする。

SQL> create pluggable database appcon1 as application container admin user admin1 identified by oracle;
SQL> alter pluggable database appcon1 open;

次に、アプリケーションコンテナ内に「アプリケーション」であるユーザやテーブルを作成する。アプリケーションには、「myapp」という名前を付けている。

この時に作成するテーブルは共通属性がデフォルトで「METADATA」となっており、メタデータリンクオブジェクトとして扱われる。

SQL> alter session set container=appcon1;
SQL> alter pluggable database application myapp begin install '1.0';
SQL> create user user1 identified by oracle;
SQL> grant create session, resource, unlimited tablespace to user1;
SQL> create table user1.tab1(col1 number primary key, col2 varchar2(8));
SQL> insert into user1.tab1 values(1, 'AAAA');
SQL> commit;
SQL> alter pluggable database application myapp end install '1.0';

アプリケーションの同期対象になるアプリケーションPDBを作成し、OPENする。

SQL> alter session set container=appcon1;
SQL> create pluggable database apppdb1 admin user admin1 identified by oracle;
SQL> alter pluggable database apppdb1 open;

そして、アプリケーションPDBに対してアプリケーションを同期させる。

SQL> alter session set container=apppdb1;
SQL> alter pluggable database application myapp sync;

アプリケーションPDBには、同期の結果ユーザやテーブルが作成され、データも反映されていることがわかる。

SQL> conn user1/oracle@oradbs.dn.home:1521/apppdb1
接続されました。
SQL> select * from tab1;

      COL1 COL2
---------- --------
         1 AAAA

今回はメタデータリンクオブジェクトとして作成しているため、アプリケーションPDB内のデータは適宜変更可能。

SQL> insert into tab1 values(2, 'BBB');

1行が作成されました。

SQL> delete from tab1 where col1=1;

1行が削除されました。

SQL> commit;

コミットが完了しました。

SQL> select * from tab1;

      COL1 COL2
---------- --------
         2 BBB

作成されたアプリケーションや、インストールの状況は cdb_applications や dba_applications ビューから確認ができる。アプリケーションルートから cdb_applications を確認すれば、アプリケーションPDB上のアプリケーションの状態がわかる。

SQL> show pdbs

    CON_ID CON_NAME                       OPEN MODE  RESTRICTED
---------- ------------------------------ ---------- ----------
         3 APPCON1                        READ WRITE NO
         4 APPPDB1                        READ WRITE NO
SQL> select app_name, app_id, app_version, app_status, con_id from cdb_applications;

APP_NAME                                     APP_ID APP_V APP_STATUS       CON_ID
---------------------------------------- ---------- ----- ------------ ----------
APP$F73BA56927DC7FDAE0531564A8C0FA0A              2 1.0   NORMAL                3
MYAPP                                             3 1.0   NORMAL                3
MYAPP                                             2 1.0   NORMAL                4

アプリケーションPDB内のユーザを削除しようとするとどうなるか

アプリケーションとして作成したユーザを削除しようとするとエラーになる。

SQL> alter session set container=apppdb1;

セッションが変更されました。

SQL> drop user user1 cascade;
drop user user1 cascade
*
行1でエラーが発生しました。:
ORA-65274: アプリケーション・アクション外部からの操作は許可されていません

アプリケーションPDB内のオブジェクトを削除しようとするとどうなるか

こちらも、同様にエラーになる。

SQL> drop table user1.tab1 purge;
drop table user1.tab1 purge
                 *
行1でエラーが発生しました。:
ORA-65274: アプリケーション・アクション外部からの操作は許可されていません

アプリケーションのアップグレード

一度アプリケーションをインストールした後にアプリケーションを修正することも可能。表現としてはアップグレードになる。

以下では、既存の表に列を追加する変更を加えている。

SQL> alter session set container=appcon1;
SQL> alter pluggable database application myapp begin upgrade '1.0' to '2.0';
SQL> alter table user1.tab1 add(col3 number);
SQL> alter pluggable database application myapp end upgrade to '2.0';

この変更をアプリケーションPDBに反映する。

SQL> alter session set container=apppdb1;
SQL> alter pluggable database application myapp sync to '2.0';

アプリケーションPDB側でも表定義が変更されたことが確認できる。

SQL> conn user1/oracle@oradbs.dn.home:1521/apppdb1
接続されました。
SQL> select * from tab1;

      COL1 COL2           COL3
---------- -------- ----------
         2 BBB

ちなみに、アップグレードを実行すると、内部的にアプリケーションルートのコンテナがクローンされる動きになる。そのため、ルートコンテナで show pdbs を実行すると自動生成名でPDBが追加されていることがわかる。

SQL> show pdbs

    CON_ID CON_NAME                       OPEN MODE  RESTRICTED
---------- ------------------------------ ---------- ----------
         2 PDB$SEED                       READ ONLY  NO
         3 APPCON1                        READ WRITE NO
         4 APPPDB1                        READ WRITE NO
         8 F843565264_3_1  ← これ        READ WRITE NO

ちなみに、このクローンされたPDBだが、消すことができないようだ。

SQL> drop pluggable database F843565264_3_1 including datafiles;
drop pluggable database F843565264_3_1 including datafiles
                        *
行1でエラーが発生しました。:
ORA-65266: アプリケーション・ルート・クローンの削除、切断、変更はいずれもできません

すでに不要になった古いバージョンのアプリケーションがある場合、無駄にPDBの領域が消費され続けることになると思うのだが、少なくとも19cのマニュアルには削除方法についての記載はなかった。

アプリケーションのアンインストール

作成済みのアプリケーションを削除するためには、アンインストールコマンドの中でユーザやオブジェクトの削除を行う。

SQL> alter session set container=appcon1;
SQL> alter pluggable database application myapp begin uninstall;
SQL> drop user user1 cascade;
SQL> alter pluggable database application myapp end uninstall;

この状態だとまだアプリケーションPDBからは削除されていない。同期を行うことで初めて削除される。

SQL> alter session set container=apppdb1;
SQL> alter pluggable database application myapp sync;
SQL> conn user1/oracle@oradbs.dn.home:1521/apppdb1
ERROR:
ORA-01017: ユーザー名/パスワードが無効です。ログオンは拒否されました。

ただ、アンインストールを行っても、アプリケーションルート上ではステータスが「UNINSTALLED」なアプリケーションとして情報は残り続ける。

SQL> alter session set container=appcon1;

セッションが変更されました。

SQL> select app_name, app_id, app_version, app_status, con_id from cdb_applications;

APP_NAME                                     APP_ID APP_V APP_STATUS       CON_ID
---------------------------------------- ---------- ----- ------------ ----------
APP$F73BA56927DC7FDAE0531564A8C0FA0A              2 1.0   NORMAL                3
MYAPP                                             3 2.0   UNINSTALLED           3
MYAPP                                             2 2.0   UNINSTALLED           4

しかも、アンインストール作業時にも、アップグレード時と同じようにアプリケーションルートのコンテナがクローンされる動作になっていて、余計なPDBがさらに増えることになる。

SQL> show pdbs

    CON_ID CON_NAME                       OPEN MODE  RESTRICTED
---------- ------------------------------ ---------- ----------
         2 PDB$SEED                       READ ONLY  NO
         3 APPCON1                        READ WRITE NO
         4 APPPDB1                        READ WRITE NO
         6 F843565264_3_2  ← これ        READ WRITE NO
         8 F843565264_3_1                 READ WRITE NO

正直、この動作はどうなんだ。アプリケーションのインストールとアンインストールを繰り返すと、どんどん不要なPDBが溜まっていく。しかも、全てのアプリケーションPDBからアンインストールしていたとしてもこれを消すことはできない。この仕様を考えたやつはアホかと思う。

SQL> drop pluggable database F843565264_3_2 including datafiles;
drop pluggable database F843565264_3_2 including datafiles
                        *
行1でエラーが発生しました。:
ORA-65266: アプリケーション・ルート・クローンの削除、切断、変更はいずれもできません

今後のリリースでエンハンスメントが入って消せるようになるのかもしれないけど、12cR2で追加されて以降、いまだにそのままということはよっぽど使う人が少なく問題提起すらされていないということなのか。なんにせよ、こういうところが実にOracleらしい。

コンテナマップの使用

複雑なうえにいまいち存在意義がわからない機能なのだが、こういうこともできるらしいということで。

改めて空のデータベースにアプリケーションコンテナの作成から始める。

SQL> create pluggable database appcon1 as application container admin user admin1 identified by oracle;
SQL> alter pluggable database appcon1 open;

アプリケーションPDBを2つ作成する。

SQL> alter session set container=appcon1;
SQL> create pluggable database apppdb1 admin user admin1 identified by oracle;
SQL> create pluggable database apppdb2 admin user admin1 identified by oracle;
SQL> alter pluggable database apppdb1 open;
SQL> alter pluggable database apppdb2 open;

アプリケーションを作成する。この時、conainer_map と containers_default を有効にしておく。

SQL> alter session set container=appcon1;
SQL> alter pluggable database application myapp begin install '1.0';
SQL> create user user1 identified by oracle;
SQL> grant create session, resource, unlimited tablespace to user1;
SQL> create table user1.tab1(col1 number primary key, col2 varchar2(8));
SQL> alter table user1.tab1 enable container_map;
SQL> alter table user1.tab1 enable containers_default;
SQL> alter pluggable database application myapp end install '1.0';

PDBにアプリケーションを同期する。

SQL> alter session set container=apppdb1;
SQL> alter pluggable database application myapp sync;
SQL> alter session set container=apppdb2;
SQL> alter pluggable database application myapp sync;

マップ表を作成する。この表はアプリケーションに含まれない別のスキーマ(今回はアプリケーションルート作成時に指定したadmin1)に作る。

SQL> alter session set container=appcon1;
SQL> grant dba to admin1;
SQL> create table admin1.pdb_map_tbl(col1 number not null) partition by range(col1) (
       partition apppdb1 values less than (10),
       partition apppdb2 values less than (100)
     );
SQL> alter pluggable database set container_map='admin1.pdb_map_tbl';

マップ表で何をやっているかというと、アプリケーションPDBの名前をパーティション名に指定し、対象の表の指定した列の値を条件としたレンジパーティションのパーティション表を作成している。

上記の場合、col1 の値が10未満であれば apppdb1、col1 の値が100未満であれば apppdb2 からデータを取得するような設定になる。

そして、それぞれのアプリケーションPDBにデータを投入する。

SQL> conn user1/oracle@oradbs.dn.home:1521/apppdb1
SQL> insert into tab1 values(1, 'A');
SQL> insert into tab1 values(2, 'B');
SQL> insert into tab1 values(3, 'C');
SQL> commit;
SQL> conn user1/oracle@oradbs.dn.home:1521/apppdb2
SQL> insert into tab1 values(10, 'X');
SQL> insert into tab1 values(20, 'Y');
SQL> insert into tab1 values(30, 'Z');
SQL> commit;

アプリケーションルートから同じ表を指定してデータをSELECTすると、全てのPDBからデータを取得して結果が表示される。CON_IDという疑似列が追加されており、どのPDBから情報をとってきているかも確認できる。

SQL> conn user1/oracle@oradbs.dn.home:1521/appcon1
接続されました。
SQL> select * from tab1;

      COL1 COL2         CON_ID
---------- -------- ----------
        10 X                 7
        20 Y                 7
        30 Z                 7
         1 A                 4
         2 B                 4
         3 C                 4

6行が選択されました。

イメージ的には、複数のアプリケーションPDBを使った表のパーティショニングといったところか。

データリンクオブジェクトの使用

アプリケーションに含めるテーブル定義の中で、sharing=data とした場合、データリンクオブジェクトとして作成される。そうすると、アプリケーションPDBはアプリケーションルートに格納されているデータを参照するようになる。

データリンクオブジェクトを含む新しいアプリケーションを作成する。

SQL> alter session set container=appcon1;
SQL> alter pluggable database application myapp2 begin install '1.0';
SQL> create user user2 identified by oracle;
SQL> grant create session, resource, unlimited tablespace to user2;
SQL> create table user2.tab1 sharing=data (col1 number primary key, col2 varchar2(8));
SQL> alter pluggable database application myapp2 end install '1.0';

アプリケーションを各PDBに同期する。

SQL> alter session set container=apppdb1;
SQL> alter pluggable database application myapp2 sync;
SQL> 
SQL> alter session set container=apppdb2;
SQL> alter pluggable database application myapp2 sync;

アプリケーションルート側でデータを投入する。

SQL> conn user2/oracle@oradbs.dn.home:1521/appcon1
SQL> insert into tab1 values(100, 'ABC');
SQL> commit;

アプリケーションPBD側でデータを確認。

SQL> conn user2/oracle@oradbs.dn.home:1521/apppdb1
接続されました。
SQL> select * from tab1;

      COL1 COL2
---------- --------
       100 ABC

SQL> conn user2/oracle@oradbs.dn.home:1521/apppdb2
接続されました。
SQL> select * from tab1;

      COL1 COL2
---------- --------
       100 ABC

アプリケーションPDB側では、個別に行を挿入したり、削除したりといったことはできない。

SQL> delete from tab1;
delete from tab1
            *
行1でエラーが発生しました。:
ORA-65097: データ・リンク表へのDMLはアプリケーション処理の外部にあります


SQL> insert into tab1 values(200, 'TEST');
insert into tab1 values(200, 'TEST')
            *
行1でエラーが発生しました。:
ORA-65097: データ・リンク表へのDMLはアプリケーション処理の外部にあります

コメント