PL/SQL の ネイティブ・コンパイル(PLSQL_CODE_TYPE=NATIVE) でループ処理を高速化してみる。(Oracle Database)
Oracle Database の PL/SQL には ネイティブ・コンパイル という機能があり、計算集中型のプロシージャーを高速化できます。
計算集中型とは DML(SELECT, INSERT, UPDATE, DELETE等)以外のロジック部分、例えばループ処理等で時間が掛かるプログラムの事を指します。
この記事では PL/SQL の ネイティブ・コンパイル でループ処理を高速化してみますやで。
彡(゚)(゚)
1. 参考マニュアル
マニュアルの記述は以下の通りです。
12.10 システム固有の実行のためのPL/SQLユニットのコンパイル
https://docs.oracle.com/cd/F19136_01/lnpls/plsql-optimization-and-tuning.html#GUID-7C4557E7-4C35-4CAB-A95D-CB96BDC6D487
:
PL/SQLのネイティブ・コンパイルによって、計算集中型のプロシージャ操作のパフォーマンスは大幅に改善されます
:
関連のパラメータは下記(PLSQL_CODE_TYPE)で INTERPRETED と NATIVE を選択します。デフォルトは INTERPRETED です。
1.277 PLSQL_CODE_TYPE
https://docs.oracle.com/cd/F19136_01/refrn/PLSQL_CODE_TYPE.html#GUID-19CE54C7-6B2A-4F3E-92C5-D30AFCD01E3F
:
PLSQL_CODE_TYPE = { INTERPRETED | NATIVE }
:
2. サンプルプログラムと実行結果(ネイティブ・コンパイル前)
下記のストアド・プロシージャーでテストしてみます。10億回ループするプログラムとなります。
ALTER SESSION SET PLSQL_CODE_TYPE = INTERPRETED; CREATE OR REPLACE PROCEDURE PRC_TEST_LOOP IS i PLS_INTEGER; j PLS_INTEGER; BEGIN j := 0; FOR i IN 1..1000000000 LOOP j := i; END LOOP; DBMS_OUTPUT.PUT_LINE('j => ' || TO_CHAR(j)); END; / SET SERVEROUTPUT ON SIZE 1000000; SET TIMING ON; EXEC PRC_TEST_LOOP;
実行結果は以下の通りです。
SQL> ALTER SESSION SET PLSQL_CODE_TYPE = INTERPRETED; Session altered. Elapsed: 00:00:00.01 SQL> CREATE OR REPLACE PROCEDURE PRC_TEST_LOOP 2 IS 3 i PLS_INTEGER; 4 j PLS_INTEGER; 5 BEGIN 6 j := 0; 7 FOR i IN 1..1000000000 8 LOOP 9 j := i; 10 END LOOP; 11 DBMS_OUTPUT.PUT_LINE('j => ' || TO_CHAR(j)); 12 END; 13 / Procedure created. Elapsed: 00:00:00.02 SQL> SQL> SET SERVEROUTPUT ON SIZE 1000000; SQL> SET TIMING ON; SQL> EXEC PRC_TEST_LOOP; j => 1000000000 PL/SQL procedure successfully completed. Elapsed: 00:00:09.54 SQL>
10秒弱程度の時間が掛かっています。
SQLトレースも取得してみました。
ALTER SESSION SET TRACEFILE_IDENTIFIER = "AYSHIBAT"; EXEC DBMS_SESSION.SESSION_TRACE_ENABLE(waits => TRUE, binds => TRUE, plan_stat => 'ALL_EXECUTIONS'); EXEC PRC_TEST_LOOP; EXEC DBMS_SESSION.SESSION_TRACE_DISABLE;
SQL> ALTER SESSION SET TRACEFILE_IDENTIFIER = "AYSHIBAT"; Session altered. Elapsed: 00:00:00.00 SQL> EXEC DBMS_SESSION.SESSION_TRACE_ENABLE(waits => TRUE, binds => TRUE, plan_stat => 'ALL_EXECUTIONS'); PL/SQL procedure successfully completed. Elapsed: 00:00:00.03 SQL> EXEC PRC_TEST_LOOP; j => 1000000000 PL/SQL procedure successfully completed. Elapsed: 00:00:11.91 SQL> EXEC DBMS_SESSION.SESSION_TRACE_DISABLE; PL/SQL procedure successfully completed. Elapsed: 00:00:00.01
SQLトレースの結果(tkprof後の抜粋)は以下の通り。CPU がブン廻ってますやね。
彡(^)(^)
SQL ID: 9azc0vuqbanzf Plan Hash: 0 BEGIN PRC_TEST_LOOP; END; call count cpu elapsed disk query current rows ------- ------ -------- ---------- ---------- ---------- ---------- ---------- Parse 1 0.00 0.00 0 0 0 0 Execute 1 11.89 11.90 0 0 0 1 Fetch 0 0.00 0.00 0 0 0 0 ------- ------ -------- ---------- ---------- ---------- ---------- ---------- total 2 11.89 11.91 0 0 0 1 Misses in library cache during parse: 0 Optimizer mode: ALL_ROWS Parsing user id: 155 Elapsed times include waiting on following events: Event waited on Times Max. Wait Total Waited ---------------------------------------- Waited ---------- ------------ SQL*Net message to client 1 0.00 0.00 SQL*Net message from client 1 0.00 0.00
3. ネイティブ・コンパイルとコンパイル後の実行結果
下記コマンドでサンプルのストアドをネイティブ・コンパイルしてみます。
ALTER SESSION SET PLSQL_CODE_TYPE = NATIVE; ALTER PROCEDURE PRC_TEST_LOOP COMPILE;
SQL> ALTER SESSION SET PLSQL_CODE_TYPE = NATIVE; Session altered. SQL> ALTER PROCEDURE PRC_TEST_LOOP COMPILE; Procedure altered.
ネイティブ・コンパイル後のプロシージャーを実行してみます。
SET SERVEROUTPUT ON SIZE 1000000; SET TIMING ON; ALTER SESSION SET TRACEFILE_IDENTIFIER = "AYSHIBAT"; EXEC DBMS_SESSION.SESSION_TRACE_ENABLE(waits => TRUE, binds => TRUE, plan_stat => 'ALL_EXECUTIONS'); EXEC PRC_TEST_LOOP; EXEC DBMS_SESSION.SESSION_TRACE_DISABLE;
SQL> SET SERVEROUTPUT ON SIZE 1000000; SQL> SET TIMING ON; SQL> ALTER SESSION SET TRACEFILE_IDENTIFIER = "AYSHIBAT"; Session altered. Elapsed: 00:00:00.00 SQL> EXEC DBMS_SESSION.SESSION_TRACE_ENABLE(waits => TRUE, binds => TRUE, plan_stat => 'ALL_EXECUTIONS'); PL/SQL procedure successfully completed. Elapsed: 00:00:00.00 SQL> EXEC PRC_TEST_LOOP; j => 1000000000 PL/SQL procedure successfully completed. Elapsed: 00:00:02.65 SQL> EXEC DBMS_SESSION.SESSION_TRACE_DISABLE; PL/SQL procedure successfully completed. Elapsed: 00:00:00.00 SQL>
3秒弱程度で完了しました。SQLトレース(tkprof後)の抜粋は以下の通り
SQL ID: 9azc0vuqbanzf Plan Hash: 0 BEGIN PRC_TEST_LOOP; END; call count cpu elapsed disk query current rows ------- ------ -------- ---------- ---------- ---------- ---------- ---------- Parse 1 0.00 0.00 0 0 0 0 Execute 1 2.51 2.63 0 0 0 1 Fetch 0 0.00 0.00 0 0 0 0 ------- ------ -------- ---------- ---------- ---------- ---------- ---------- total 2 2.51 2.63 0 0 0 1 Misses in library cache during parse: 1 Optimizer mode: ALL_ROWS Parsing user id: 155 Elapsed times include waiting on following events: Event waited on Times Max. Wait Total Waited ---------------------------------------- Waited ---------- ------------ SQL*Net message to client 1 0.00 0.00 SQL*Net message from client 1 0.00 0.00
CPU時間が短くなっているのが SQLトレース でも確認できました。 彡(^)(^)
4. まとめ
ネイティブ・コンパイルでPL/SQLのロジック部分が高速化するのを確認できました。
ストアド・プロシージャ(or ファンクション/パッケージ)のロジック部分で CPU がブン回っているようなら、試してみて下さいね。
彡(^)(^)
V$SQL_PLAN の OTHER_XML列 に格納された XMLデータ を整形して出力してみる(Oracle Database)
V$SQL_PLAN は Oracle Database の実行計画に関する情報が格納されていますが、該当SQLに関連する情報が格納されています。
SET LONG 1000000; SET LONGC 1000000; SET LINESIZE 100; COLUMN OTHER_XML_VALUE FORMAT A100; SELECT OTHER_XML FROM V$SQL_PLAN S WHERE S.SQL_ID = 'f9r2y6xdz6t84' AND S.CHILD_NUMBER = 4; OTHER_XML ---------------------------------------------------------------------------------------------------- <other_xml><info type="cardinality_feedback" note="y">yes</info><info type="db_version">12.2.0.1</in fo><info type="parse_schema"><![CDATA["AYSHIBAT"]]></info><info type="dynamic_sampling" note="y">11< /info><info type="plan_hash_full">247910413</info><info type="plan_hash">5458159</info><info type="p lan_hash_2">247910413</info><outline_data><hint><![CDATA[IGNORE_OPTIM_EMBEDDED_HINTS]]></hint><hint> <![CDATA[OPTIMIZER_FEATURES_ENABLE('12.2.0.1')]]></hint><hint><![CDATA[DB_VERSION('12.2.0.1')]]></hi nt><hint><![CDATA[OPT_PARAM('optimizer_dynamic_sampling' 11)]]></hint><hint><![CDATA[ALL_ROWS]]></hi nt><hint><![CDATA[OUTLINE_LEAF(@"SEL$1")]]></hint><hint><![CDATA[FULL(@"SEL$1" "B"@"SEL$1")]]></hint ><hint><![CDATA[FULL(@"SEL$1" "A"@"SEL$1")]]></hint><hint><![CDATA[LEADING(@"SEL$1" "B"@"SEL$1" "A"@ "SEL$1")]]></hint><hint><![CDATA[USE_HASH(@"SEL$1" "A"@"SEL$1")]]></hint></outline_data></other_xml>
このXMLデータを整形して出力してみますやで彡(゚)(゚)
1. テストデータ
DBMS_XPLAN の以下の実行計画(SQL_ID f9r2y6xdz6t84, child number 4)の OTHER_XML列 を使用します。
SQL> SET AUTOTRACE OFF; SQL> SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY_CURSOR('f9r2y6xdz6t84', NULL, 'ADVANCED ALLSTATS LAST ADAPTIVE')); : : SQL_ID f9r2y6xdz6t84, child number 4 ------------------------------------- SELECT /*+ MONITOR */ B.ITEM_NAME , TRUNC(A.ORDER_DATE, 'DD') AS ORDER_DAILY , COUNT(*) FROM ORDER_TBL A , ITEM_TBL B WHERE A.ITEM_NO = B.ITEM_NO -- AND B.ITEM_NO BETWEEN 1 AND 100 AND TO_CHAR(B.REGIST_DATE, 'YYYYMMDD') = '20120801' GROUP BY B.ITEM_NAME, TRUNC(A.ORDER_DATE, 'DD') ORDER BY 1 Plan hash value: 5458159 ------------------------------------------------------------------------------------------------------------------------------------------------------ | Id | Operation | Name | Starts | E-Rows |E-Bytes| Cost (%CPU)| E-Time | A-Rows | A-Time | Buffers | OMem | 1Mem | Used-Mem | ------------------------------------------------------------------------------------------------------------------------------------------------------ | 0 | SELECT STATEMENT | | 1 | | | 2435 (100)| | 261 |00:00:00.51 | 8922 | | | | | 1 | SORT GROUP BY | | 1 | 261 | 6786 | 2435 (1)| 00:00:01 | 261 |00:00:00.51 | 8922 | 18432 | 18432 |16384 (0)| |* 2 | HASH JOIN | | 1 | 33601 | 853K| 2433 (1)| 00:00:01 | 26000 |00:00:00.50 | 8922 | 2171K| 2171K| 1135K (0)| |* 3 | TABLE ACCESS FULL| ITEM_TBL | 1 | 10 | 150 | 26 (4)| 00:00:01 | 10 |00:00:00.01 | 91 | | | | | 4 | TABLE ACCESS FULL| ORDER_TBL | 1 | 2600K| 27M| 2399 (1)| 00:00:01 | 2600K|00:00:00.14 | 8831 | | | | ------------------------------------------------------------------------------------------------------------------------------------------------------ Query Block Name / Object Alias (identified by operation id): ------------------------------------------------------------- 1 - SEL$1 3 - SEL$1 / B@SEL$1 4 - SEL$1 / A@SEL$1 Outline Data ------------- /*+ BEGIN_OUTLINE_DATA IGNORE_OPTIM_EMBEDDED_HINTS OPTIMIZER_FEATURES_ENABLE('12.2.0.1') DB_VERSION('12.2.0.1') OPT_PARAM('optimizer_dynamic_sampling' 11) ALL_ROWS OUTLINE_LEAF(@"SEL$1") FULL(@"SEL$1" "B"@"SEL$1") FULL(@"SEL$1" "A"@"SEL$1") LEADING(@"SEL$1" "B"@"SEL$1" "A"@"SEL$1") USE_HASH(@"SEL$1" "A"@"SEL$1") END_OUTLINE_DATA */ Predicate Information (identified by operation id): --------------------------------------------------- 2 - access("A"."ITEM_NO"="B"."ITEM_NO") 3 - filter(TO_CHAR(INTERNAL_FUNCTION("B"."REGIST_DATE"),'YYYYMMDD')='20120801') Column Projection Information (identified by operation id): ----------------------------------------------------------- 1 - (#keys=2) "B"."ITEM_NAME"[VARCHAR2,100], TRUNC(INTERNAL_FUNCTION("A"."ORDER_DATE"),'fmdd')[8], COUNT(*)[22] 2 - (#keys=1) "B"."ITEM_NAME"[VARCHAR2,100], "A"."ORDER_DATE"[DATE,7] 3 - (rowset=256) "B"."ITEM_NO"[NUMBER,22], "B"."ITEM_NAME"[VARCHAR2,100] 4 - "A"."ITEM_NO"[NUMBER,22], "A"."ORDER_DATE"[DATE,7] Note ----- - dynamic statistics used: dynamic sampling (level=AUTO) - statistics feedback used for this statement
2. 一階層目と二階層目のXMLデータを出力してみる。
まず一階層目のXMLデータを出力してみます。
SET LONG 1000000; SET LONGC 1000000; SET LINESIZE 100; COLUMN XML_VALUE FORMAT A100; SELECT INFO.XML_VALUE FROM ( SELECT EXTRACT(VALUE(p), '/*') AS XML_VALUE FROM V$SQL_PLAN S, TABLE(XMLSEQUENCE(EXTRACT(XMLTYPE.CREATEXML(OTHER_XML), '/other_xml/*'))) p WHERE S.SQL_ID = 'f9r2y6xdz6t84' AND S.CHILD_NUMBER = 4 ) INFO; XML_VALUE ---------------------------------------------------------------------------------------------------- <info type="cardinality_feedback" note="y">yes</info> <info type="db_version">12.2.0.1</info> <info type="parse_schema"><![CDATA["AYSHIBAT"]]></info> <info type="dynamic_sampling" note="y">11</info> <info type="plan_hash_full">247910413</info> <info type="plan_hash">5458159</info> <info type="plan_hash_2">247910413</info> <outline_data><hint><![CDATA[IGNORE_OPTIM_EMBEDDED_HINTS]]></hint><hint><![CDATA[OPTIMIZER_FEATURES_ ENABLE('12.2.0.1')]]></hint><hint><![CDATA[DB_VERSION('12.2.0.1')]]></hint><hint><![CDATA[OPT_PARAM( 'optimizer_dynamic_sampling' 11)]]></hint><hint><![CDATA[ALL_ROWS]]></hint><hint><![CDATA[OUTLINE_LE AF(@"SEL$1")]]></hint><hint><![CDATA[FULL(@"SEL$1" "B"@"SEL$1")]]></hint><hint><![CDATA[FULL(@"SEL$1 " "A"@"SEL$1")]]></hint><hint><![CDATA[LEADING(@"SEL$1" "B"@"SEL$1" "A"@"SEL$1")]]></hint><hint><![C DATA[USE_HASH(@"SEL$1" "A"@"SEL$1")]]></hint></outline_data>
SQL を少し編集して二階層目の情報を出力してみます。
SET LONG 1000000; SET LONGC 1000000; SET LINESIZE 100; COLUMN INFO.XML_VALUE FORMAT A100; SELECT INFO.XML_VALUE FROM ( SELECT EXTRACT(VALUE(p), '/*') AS XML_VALUE FROM V$SQL_PLAN S, TABLE(XMLSEQUENCE(EXTRACT(XMLTYPE.CREATEXML(OTHER_XML), '/other_xml/*/*'))) p WHERE S.SQL_ID = 'f9r2y6xdz6t84' AND S.CHILD_NUMBER = 4 ) INFO; XML_VALUE ---------------------------------------------------------------------------------------------------- <hint><![CDATA[IGNORE_OPTIM_EMBEDDED_HINTS]]></hint> <hint><![CDATA[OPTIMIZER_FEATURES_ENABLE('12.2.0.1')]]></hint> <hint><![CDATA[DB_VERSION('12.2.0.1')]]></hint> <hint><![CDATA[OPT_PARAM('optimizer_dynamic_sampling' 11)]]></hint> <hint><![CDATA[ALL_ROWS]]></hint> <hint><![CDATA[OUTLINE_LEAF(@"SEL$1")]]></hint> <hint><![CDATA[FULL(@"SEL$1" "B"@"SEL$1")]]></hint> <hint><![CDATA[FULL(@"SEL$1" "A"@"SEL$1")]]></hint> <hint><![CDATA[LEADING(@"SEL$1" "B"@"SEL$1" "A"@"SEL$1")]]></hint> <hint><![CDATA[USE_HASH(@"SEL$1" "A"@"SEL$1")]]></hint>
3. OUTLINEヒントを抽出してみる。
下記のような SQL にすると DBMS_XPLAN の Outline Dataセクション に相当する情報(OUTLINEヒント)を出力できます。
SELECT HINT.HINT_VALUE FROM ( SELECT EXTRACTVALUE(VALUE(p), '/hint') AS HINT_VALUE FROM V$SQL_PLAN S, TABLE(XMLSEQUENCE(EXTRACT(XMLTYPE.CREATEXML(OTHER_XML), '/other_xml/outline_data/hint'))) p WHERE S.SQL_ID = 'f9r2y6xdz6t84' AND S.CHILD_NUMBER = 4 ) HINT; HINT_VALUE ---------------------------------------------------------------------------------------------------- IGNORE_OPTIM_EMBEDDED_HINTS OPTIMIZER_FEATURES_ENABLE('12.2.0.1') DB_VERSION('12.2.0.1') OPT_PARAM('optimizer_dynamic_sampling' 11) ALL_ROWS OUTLINE_LEAF(@"SEL$1") FULL(@"SEL$1" "B"@"SEL$1") FULL(@"SEL$1" "A"@"SEL$1") LEADING(@"SEL$1" "B"@"SEL$1" "A"@"SEL$1") USE_HASH(@"SEL$1" "A"@"SEL$1")
4. まとめ
XML DB は難解……しかも今回使用したファンクションは非推奨となっています。 OTHER_XML列に格納されているデータも含めて自己責任で使用して下さいませ。
EXTRACT (XML) https://docs.oracle.com/cd/F19136_01/sqlrf/EXTRACT-XML.html#GUID-593295AA-4F46-4D75-B8DC-E7BCEDB1D4D7
EXTRACTVALUE https://docs.oracle.com/cd/F19136_01/sqlrf/EXTRACTVALUE.html#GUID-20AB974B-7544-4F44-B539-787FB6145680
まあ今時は JSON だとは思いますが。 彡(゚)(゚)
OCI の ネットワーク・ソース で コンソール に ログイン可能 な IP を制限する(Oracle Cloud Infrastructure)
表題の通り OCI の ネットワーク・ソース で コンソール に ログイン可能 な IP を制限してみますやで。
彡(゚)(゚)
1. 参考マニュアル
以下のOCIマニュアルを参考にしています。
https://docs.oracle.com/ja-jp/iaas/Content/Identity/Tasks/managingnetworksources.htm
2. ネットワーク・ソース の 作成
画面左上ハンバーガーメニュー → Identity & Security → Network Sources を選択します。
Create Network Sourceボタンをクリックして接続を許可する CIDR(or IP) を入力します。
Createボタンをクリックしてネットワーク・ソースを作成します。
3. 認証設定の管理 の 変更
画面左上ハンバーガーメニュー → Identity & Security → Authentication Settings を選択します。
Edit Authentication Settingsボタンをクリックして認証設定を編集します。
- で作成した ネットワーク・ソース を選択して Save Changesボタンをクリックします。
4. ログインの試行(許可されていないネットワークより)
許可されていないネットワークからのOCIログインを試してみます。
下記のようなエラーが発生してログインできません。
5. ログインの試行(許可されたネットワークより)
許可されたネットワークからOCIにログインしてみます。
正常にログインできます。
6. まとめ
すでに同様の記事は幾つもあるのですが、復習のためにワイ自身でもやってみました。
マニュアルにも記載があるのですが設定をしくじるとログイン不可になって何もできなくなります。
注意 ネットワーク・ソース制限を設定する前に、許可されたネットワークが使用できない場合に テナンシへのアクセスを可能にするAPIキーが設定されていることを確認してください。 APIキーを設定せず、許可されたネットワークを使用できない場合は、Oracleサポートに 連絡するまですべてのユーザーがテナンシからロックアウトされます。 API署名キーの設定の詳細は、必要なキーとOCIDを参照してください。
管理ユーザーのAPIキー発行やサポートサイト(MOS)へのログイン確認など、
あらかじめ対策をしておいて下さいね彡(^)(^)
DDLトリガー と イベント属性ファンクション で CREATE されるオブジェクトのオーナー名とオブジェクト名を記録する。(Oracle Database)
表題の通り Oracle Database の DDLトリガー と イベント属性ファンクションで、CREATE されるオブジェクトのオーナー名とオブジェクト名を記録してみますやで。
彡(゚)(゚)
1. 参考マニュアル
以下のマニュアルを参考にしました。イベント属性ファンクションが肝ですね。
Oracle Database データベース PL/SQL言語リファレンス 19c
9.15.1 イベント属性ファンクション
https://docs.oracle.com/cd/F19136_01/lnpls/plsql-triggers.html#GUID-266DBF6D-AA74-490C-ADE5-962C10708C2D
ora_dict_obj_name ... VARCHAR2(128): DDL操作が発生したディクショナリ・オブジェクトの名前
ora_dict_obj_owner ... VARCHAR2(128): DDL操作が発生したディクショナリ・オブジェクトの所有者
ora_dict_obj_type ... VARCHAR2(20) : DDL操作が発生したディクショナリ・オブジェクトの型
9.5.3 INSTEAD OF CREATEトリガー
https://docs.oracle.com/cd/F19136_01/lnpls/plsql-triggers.html#GUID-7B32FE4B-AB8E-48B0-A74C-599584A485A7
14.12 CREATE TRIGGER文 ※構文図
https://docs.oracle.com/cd/F19136_01/lnpls/CREATE-TRIGGER-statement.html#GUID-AF9E33F1-64D1-4382-A6A4-EC33C36F237B
2. 事前準備(記録用テーブル と DDLトリガー作成)
記録用テーブル と DDLトリガー を作成します。SYSユーザーで作成します。
今回は ayshibatスキーマ の CREATE を記録するようにしました。
CONNECT SYS/xxxxxxxx@ORCL AS SYSDBA CREATE TABLE ayshibat.tbl_create_object_logging ( OBJECT_OWNER VARCHAR2(128) , OBJECT_NAME VARCHAR2(128) , OBJECT_TYPE VARCHAR2(23) , CREATE_TIMESTAMP TIMESTAMP ); CREATE OR REPLACE TRIGGER ayshibat.trg_create_object_logging INSTEAD OF CREATE ON ayshibat.SCHEMA BEGIN INSERT INTO ayshibat.tbl_create_object_logging VALUES ( ora_dict_obj_owner , ora_dict_obj_name , ora_dict_obj_type , SYSTIMESTAMP ); END; /
3. オブジェクト作成 と 記録結果の確認
対象スキーマ(今回は ayshibatスキーマ)でテーブルを CREATE してみます。
CONNECT ayshibat/xxxxxxxx@ORCL; CREATE TABLE TAB1(ID NUMBER); ALTER SESSION SET NLS_TIMESTAMP_FORMAT = 'YYYY/MM/DD HH24:MI:SS.FF'; SET LINESIZE 170; COLUMN OBJECT_OWNER FORMAT A30; COLUMN OBJECT_NAME FORMAT A30; COLUMN CREATE_TIMESTAMP FORMAT A30; SELECT * FROM ayshibat.tbl_create_object_logging;
結果は以下の通りです。
SQL> CONNECT ayshibat/xxxxxxxx@ORCL; Connected. SQL> CREATE TABLE TAB1(ID NUMBER); Table created. SQL> ALTER SESSION SET NLS_TIMESTAMP_FORMAT = 'YYYY/MM/DD HH24:MI:SS.FF'; Session altered. SQL> SET LINESIZE 170; SQL> COLUMN OBJECT_OWNER FORMAT A30; SQL> COLUMN OBJECT_NAME FORMAT A30; SQL> COLUMN CREATE_TIMESTAMP FORMAT A30; SQL> SELECT * FROM ayshibat.tbl_create_object_logging; OBJECT_OWNER OBJECT_NAME OBJECT_TYPE CREATE_TIMESTAMP ------------------------------ ------------------------------ ----------------------- ------------------------------ AYSHIBAT TAB1 TABLE 2022/05/30 10:11:44.580414
オーナー名とオブジェクト名を記録できました。
4. まとめ
CREATE されたオブジェクトのオーナー名やオブジェクト名を記録できました。
当初はイベント属性ファンクションに辿り着けず、教えて頂いたN家さんに感謝感激雨霰
彡(^)(^)アリガトゴザマース
OCI API Gateway の機能と OCI Vault のシークレットで OCI Functions を保護(認証)してみる。(Oracle Cloud Infrastructure)
OCI API Gateway には認証機能が付いています。今回は API Gateway の認証機能を使用して OCI Functions を保護(認証)してみますやで。 彡(゚)(゚)
1. やりたい事&元ネタ
元ネタは下記の記事となります。
Oracle Functionsを利用したAPI Gatewayの認証
https://oracle-japan.github.io/ocitutorials/cloud-native/functions-apigateway-for-intermediates/
上記記事の 認証用Functions を改変して、リクエストヘッダーにセットした文字列(トークン) と OCI Vault のシークレットを突合する、以下のような処理を実装してみます。
(1). API Gateway の エンドポイント をコールする。
(2). API Gateway から認証用の Functions がコールされる。
(3). 認証用Functions で OCI Vault のシークレットを取り出して、ヘッダーの文字列(トークン)と突合する。
(4). ヘッダーの文字列(トークン) と OCI Vault のシークレット が一致した場合は本体の Functions をコールする。
接続トポロジは以下の通りです。
2. シンプルな Functions の作成
詳細は省略します。下記記事を参照して下さい。
https://qiita.com/ora_gonsuke777/items/a9bb52faadcb9f2af38e
下記の結果が得られるように少し改変してみました。wai ga AYU ya!彡(^)(^)
$ fn invoke ayu-functions1 ayu-app Hello, wai ga AYU ya!
3. API Gateway から OCI Functions の呼び出し設定
これも詳細は省略します。下記記事を参照してシンプルな Functions を API Gateway からコールできるようにしておいて下さい。
https://qiita.com/ora_gonsuke777/items/e2cc19d38f056241fb07
4. OCI Vault, キー, シークレットの作成
下記記事を参照して、認証に使用する文字列(トークン)を OCI Vault のシークレットとして登録して下さい。
https://qiita.com/kenwatan/items/5867a06ef6a00749dcf0
作成したシークレットの OCID はこの後使用するので、メモしておいて下さい。
5. 認証用Functions のダウンロード(git clone)と YAML編集、ビルド
認証用Functions のサンプルを下記に置いておきました。
https://github.com/gonsuke777/Functions
Functions の Cloud Shell から git clone でダウンロードします。
git clone https://github.com/gonsuke777/Functions
ダウンロード後に func.yaml を編集して、シークレットの OCID を登録したものに書き換えて下さい。
cd Functions/hello-java/ vi vi
schema_version: 20180708 name: hello-java version: 0.0.46 runtime: java build_image: fnproject/fn-java-fdk-build:jdk17-1.0.146 run_image: fnproject/fn-java-fdk:jre17-1.0.146 cmd: com.example.fn.HelloFunction::handleRequest timeout: 60 config: SECRET1_ID: ocid1.vaultsecret.oc1.ap-tokyo-1.xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
yaml編集後にビルド&デプロイします。
fn -v deploy --app ayu-functions1
デプロイされた Functions の OCID を控えておきます。
6. 動的グループの作成と Functions からの OCI Vault読取ポリシー(権限)付与(リソース・プリンシプル)
認証用Functions が OCI Vault のシークレットを読み取れるように権限を付与します。(リソース・プリンシプル)
- 動的グループ(ayu-dynamic-group2)のマッチングルール ※Functions の OCID を指定
All {resource.id = 'ocid1.fnfunc.oc1.ap-tokyo-1.xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx'}
- 動的グループ(ayu-dynamic-group2)に付与したポリシー(権限) ※シークレットの読み取り権限を付与
allow dynamic-group ayu-dynamic-group2 to read secret-family in compartment xxxxx_compartment
- OCI Vault のポリシー(権限)をコンパートメントに付与
allow service VaultSecret to use vaults in compartment ayu_compartment allow service VaultSecret to use keys in compartment ayu_compartment
その他、本記事のメインではありませんが API Gateway用に下記の動的グループ/ポリシー(権限)を付与しています。
ALL {resource.type = 'ApiGateway', resource.id = 'ocid1.apigateway.oc1.ap-tokyo-1.xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx'}
ALLOW any-user to use functions-family in compartment ayu_compartment where ALL { request.principal.type= 'ApiGateway', request.resource.compartment.id = 'ocid1.compartment.oc1..xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx' }
7. 認証用Functions の動作確認(Cloud Shell)
Functions の Cloud Shell からデプロイした認証用Functions の動作確認をしてみます。
# Case1...No token. fn invoke ayu-functions1 hello-java # Case2...Invalid token echo "{\"type\":\"TOKEN\",\"token\":\"xxxxxxxxx\"}" | fn invoke ayu-functions1 hello-java | jq -a # Case3...Correct token echo "{\"type\":\"TOKEN\",\"token\":\"yyyyyyyyy\"}" | fn invoke ayu-functions1 hello-java | jq -a
ビルドや権限付与が上手く行っていれば、それぞれ異なる結果を返却します。
$ fn invoke ayu-functions1 hello-java Error invoking function. status: 502 message: function failed $ echo "{\"type\":\"TOKEN\",\"token\":\"xxxxxxxxx\"}" | fn invoke ayu-functions1 hello-java | jq -a { "active": false, "principal": null, "scope": null, "expiresAt": "2020-04-30T10:15:30+01:00", "wwwAuthenticate": "Bearer realm=\"example.com\", error=\"invalid token\", error_description=\"token should be \"Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1nHyDtTwR3SEJ3z489...\"\"", "clientId": null, "context": { "email": "john.doe@example.com" } } $ echo "{\"type\":\"TOKEN\",\"token\":\"yyyyyyyyy\"}" | fn invoke ayu-functions1 hello-java | jq -a { "active": true, "principal": "https://example.com/users/jdoe", "scope": [ "list:hello", "read:hello", "create:hello", "update:hello", "delete:hello", "someScope" ], "expiresAt": "2022-04-10T09:47:16.703Z", "wwwAuthenticate": null, "clientId": "host123", "context": { "email": "john.doe@example.com" } } $
8. API Gateway Deployment の作成
API Gateway の Deployment を作成します。この時に認証用Functionsを指定します。
変数名 | 入力する値 |
---|---|
NAME | 任意の名前 |
PATH PREFIX | 任意のPREFIXを指定 |
AUTHENTICATION TYPE | Custorm |
CONPARTMENT | 認証用Functionsを作成したコンパートメント |
FUNCTION NAME | デプロイ済みの認証用Functions |
AUTHENTICATION TOKEN | ヘッダー |
HEADER NAME | token |
PATH | 任意のアプリケーションパス |
METHODS | 今回は GET を指定 |
TYPE | Oracle Functions を選択 |
APPPLICATION IN xxx_COMPARTMENT | 本体のFunctionsを指定 |
FUNCTION NAME | 呼び出す本体のFunctions |
9. ソースコードの簡単な解説
Functions の初期化処理(FnConfigurationアノテーション)で yaml からシークレットの OCID を取得しています。
@FnConfiguration public void setUp(RuntimeContext ctx) throws Exception { config = ctx.getConfiguration(); secret1Id = config.get("SECRET1_ID"); String version = System.getenv("OCI_RESOURCE_PRINCIPAL_VERSION"); if( version != null ) { provider = ResourcePrincipalAuthenticationDetailsProvider.builder().build(); } else { try { provider = new ConfigFileAuthenticationDetailsProvider("~/.oci/config", "DEFAULT"); } catch (IOException e) { e.printStackTrace(); } } }
Autonomous Database に Functions で接続する記事のソースをほぼ流用させて頂いています。OCID を基にシークレットに格納されたトークン(文字列)を取得しています。認証用Functions には前述の通りリソース・プリンシプルでシークレットの読取権限(ポリシー)を付与しています。
private String getSecret(String secretOcid) { try (SecretsClient secretsClient = new SecretsClient(provider)) { //region setting secretsClient.setRegion(Region.AP_TOKYO_1); GetSecretBundleRequest getSecretBundleRequest = GetSecretBundleRequest .builder() .secretId(secretOcid) .stage(GetSecretBundleRequest.Stage.Current) .build(); GetSecretBundleResponse getSecretBundleResponse = secretsClient .getSecretBundle(getSecretBundleRequest); Base64SecretBundleContentDetails base64SecretBundleContentDetails = (Base64SecretBundleContentDetails) getSecretBundleResponse. getSecretBundle().getSecretBundleContent(); byte[] secretValueDecoded = Base64.decodeBase64(base64SecretBundleContentDetails.getContent()); return new String(secretValueDecoded); } catch (Exception e) { throw new RuntimeException("Couldn't get content from secret - " + e.getMessage(), e); } }
ヘッダーのトークンとシークレットの文字列を比較して、合致した場合は正常終了の結果を返却しています。
if (secret1.equals(input.token)) { result = trueResult(); } else { result.wwwAuthenticate = "Bearer realm=\"example.com\", error=\"invalid token\", error_description=\"token should be \"Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1nHyDtTwR3SEJ3z489...\"\""; return result; }
結果を返却する部分は元ネタをそのまま流用しています。
https://github.com/oracle-japan/function-authorizer-for-apigw
private Result trueResult() { Result trueResult = new Result(); trueResult.active = true; trueResult.principal = "https://example.com/users/jdoe"; trueResult.scope = new String[]{"list:hello", "read:hello", "create:hello", "update:hello", "delete:hello", "someScope"}; trueResult.clientId = "host123"; trueResult.expiresAt = new Date().toInstant().plusMillis(60000).toString(); Map<String, Object> contextMap = new HashMap<>(); contextMap.put("email", "john.doe@example.com"); trueResult.context = contextMap; return trueResult; } private Result falseResult() { Result falseResult = new Result(); falseResult.active = false; falseResult.expiresAt = "2020-04-30T10:15:30+01:00"; Map<String, Object> contextMap = new HashMap<>(); contextMap.put("email", "john.doe@example.com"); falseResult.context = contextMap; falseResult.wwwAuthenticate = "Bearer realm=\"example.com\""; return falseResult; }
10. PC端末 の PowerShell から API Gateway Deployment のエンドポイント(URL)をコール
API Gateway Deployment画面から作成した Deployment のエンドポイント(URL)をコピーします。
※画面からコピーできるのはPREFIXまでなので、APIGWデプロイ時のアプリケーションパスを追加しておきます。
PowerShell から API Gateway Deployment エンドポイント(URL) をコールしてみます。
# Case1...No header $headers = @{} curl -Headers $headers https://xxxxxxxxxxxxxxxxxxxxxxxxxx.apigateway.ap-tokyo-1.oci.customer-oci.com/hello/ayuapp # Case2...No token $headers = @{} $headers["type"] = "TOKEN" curl -Headers $headers https://xxxxxxxxxxxxxxxxxxxxxxxxxx.apigateway.ap-tokyo-1.oci.customer-oci.com/hello/ayuapp # Case3...Invalid token $headers = @{} $headers["type"] = "TOKEN" $headers["token"] = "xxxxxxxx" curl -Headers $headers https://xxxxxxxxxxxxxxxxxxxxxxxxxx.apigateway.ap-tokyo-1.oci.customer-oci.com/hello/ayuapp # Case4...Correct token $headers = @{} $headers["type"] = "TOKEN" $headers["token"] = "yyyyyyyy" curl -Headers $headers https://xxxxxxxxxxxxxxxxxxxxxxxxxx.apigateway.ap-tokyo-1.oci.customer-oci.com/hello/ayuapp
以下のように、正しいトークンを指定すると本体のFunctionsの実行結果("Hello, wai ga AYU ya!")を得られました。
PS C:\Users\AYSHIBAT> $headers = @{} PS C:\Users\AYSHIBAT> curl -Headers $headers https://xxxxxxxxxxxxxxxxxxxxxxxxxx.apigateway.ap-tokyo-1.oci.customer-oci.com/hello/ayuapp curl : リモート サーバーがエラーを返しました: (401) 許可されていません 発生場所 行:1 文字:1 + curl -Headers $headers https://xxxxxxxxxxxxxxxxxxxxxxxxxx.apigateway. ... + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + CategoryInfo : InvalidOperation: (System.Net.HttpWebRequest:HttpWebRequest) [Invoke-WebRequest]、WebException PS C:\Users\AYSHIBAT> $headers = @{} PS C:\Users\AYSHIBAT> $headers["type"] = "TOKEN" PS C:\Users\AYSHIBAT> curl -Headers $headers https://xxxxxxxxxxxxxxxxxxxxxxxxxx.apigateway.ap-tokyo-1.oci.customer-oci.com/hello/ayuapp curl : リモート サーバーがエラーを返しました: (401) 許可されていません 発生場所 行:1 文字:1 + curl -Headers $headers https://xxxxxxxxxxxxxxxxxxxxxxxxxx.apigateway. ... + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + CategoryInfo : InvalidOperation: (System.Net.HttpWebRequest:HttpWebRequest) [Invoke-WebRequest]、WebException + FullyQualifiedErrorId : WebCmdletWebResponseException,Microsoft.PowerShell.Commands.InvokeWebRequestCommand PS C:\Users\AYSHIBAT> $headers = @{} PS C:\Users\AYSHIBAT> $headers["type"] = "TOKEN" PS C:\Users\AYSHIBAT> $headers["token"] = "xxxxxxxx" PS C:\Users\AYSHIBAT> curl -Headers $headers https://xxxxxxxxxxxxxxxxxxxxxxxxxx.apigateway.ap-tokyo-1.oci.customer-oci.com/hello/ayuapp curl : リモート サーバーがエラーを返しました: (401) 許可されていません 発生場所 行:1 文字:1 + curl -Headers $headers https://xxxxxxxxxxxxxxxxxxxxxxxxxx.apigateway. ... + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + CategoryInfo : InvalidOperation: (System.Net.HttpWebRequest:HttpWebRequest) [Invoke-WebRequest]、WebException + FullyQualifiedErrorId : WebCmdletWebResponseException,Microsoft.PowerShell.Commands.InvokeWebRequestCommand PS C:\Users\AYSHIBAT> $headers = @{} PS C:\Users\AYSHIBAT> $headers["type"] = "TOKEN" PS C:\Users\AYSHIBAT> $headers["token"] = "yyyyyyyy" PS C:\Users\AYSHIBAT> curl -Headers $headers https://xxxxxxxxxxxxxxxxxxxxxxxxxx.apigateway.ap-tokyo-1.oci.customer-oci.com/hello/ayuapp StatusCode : 200 StatusDescription : OK Content : Hello, wai ga AYU ya! RawContent : HTTP/1.1 200 OK Connection: keep-alive opc-request-id: /7516FB0E1242D7AB12CE7F9DFEAACCBB/708B115FEB126E2B96E78CE9E87376C6 X-XSS-Protection: 1; mode=block Strict-Transport-Security: max-age=3153600... Forms : {} Headers : {[Connection, keep-alive], [opc-request-id, /7516FB0E1242D7AB12CE7F9DFEAACCBB/708B115FEB126E2B96E78 CE9E87376C6], [X-XSS-Protection, 1; mode=block], [Strict-Transport-Security, max-age=31536000]...} Images : {} InputFields : {} Links : {} ParsedHtml : mshtml.HTMLDocumentClass RawContentLength : 21 PS C:\Users\AYSHIBAT>
11. まとめ
リクエストのヘッダーに文字列を埋め込むだけというとても簡易的な実装ですが、API Gateway と OCI Vault の機能で Functions を保護(認証)できました。
次回はもう少し凝った実装をしてみたいですやね。彡(^)(^)
12. 参考
下記の記事やマニュアルを参考にしました。
- APIデプロイメントへの認証と認可の追加
- 認可プロバイダ・ファンクションを使用したAPIデプロイメントへの認証および認可の追加
https://docs.oracle.com/ja-jp/iaas/Content/APIGateway/Tasks/apigatewayusingauthorizerfunction.htm
https://oracle-japan.github.io/ocitutorials/cloud-native/functions-apigateway-for-intermediates/
https://qiita.com/shukawam/items/107987bba2e44222c3aa
- [OCI] OCIシークレットを使ってOracle FunctionsからAutonomous DBに接続してみた。
https://qiita.com/kenwatan/items/5867a06ef6a00749dcf0
OCI GoldenGate(OCI GG) で DBCS SE から DBCS EE にレプリケーションしてみる。(Oracle Cloud Infrastructure)
直近はお仕事で OCI GoldenGate を触ることが多いので、記事を書いてみます。 従量課金なのがエエですね。
表題のとおり OCI GoldenGate で Oracle Database(DBCS) のレプリケーションを設定してみるやで。
彡(^)(^)
下記マニュアルの手順(クイックスタート)でやってみます。
同じリージョン内のクラウド・データベース間でのデータのレプリケート
https://docs.oracle.com/ja-jp/iaas/goldengate/doc/replicating-data-two-cloud-databases.html
1. OCI GoldenGate とは?
OCI GoldenGate は OCI上で動作する論理レプリケーション/データ連携のツール/PaaSとなります。
Oracle Cloud Infrastructure GoldenGateの概要
https://docs.oracle.com/ja-jp/iaas/goldengate/doc/overview-goldengate.html
オンプレの GoldenGate は 様々なデータベース/データストアに対応しますが、
現時点(2022年3月末)ではOCI GoldenGate は Oracle Database にのみ対応しています。
データベース登録の管理
https://docs.oracle.com/ja-jp/iaas/goldengate/doc/database-registrations.html
サポートされるデータベース
:
次のデータベースは、OCI GoldenGateをソースまたはターゲットとして使用することが保証されています:
Oracle Database 11.2.0.4、12.1.0.2以上
Oracle Exadata
Oracle ExadataCS
Oracle Autonomous Transaction Processing
Oracle Autonomous Data Warehouse
:
2. 検証環境の構成(OCI)
OCI DBCS SE(11.2.0.4, Source) ⇒ OCI GG ⇒ OCI DBCS EE(19.14, Target) の構成でレプリケーションします。
これらの構成に必要な コンパートメント/VCN/Subnet/DBCS等は作成済みのものとします。
また DBCS SE(11.2.0.4, Source) にはマニュアル記載の推奨パッチを適用しておきます。
https://docs.oracle.com/ja-jp/iaas/goldengate/doc/database-registrations.html
:
Oracleサポートが推奨するように、OCI GoldenGateで使用するデータベースの最新パッチを適用してください。
Oracle 11gの推奨パッチ
https://support.oracle.com/epmos/faces/DocumentDisplay?id=1557031.1
:
伝播対象のテーブルも予め作成しておきます。Source と Target の両方に作成しておきます。
CREATE TABLE AYSHIBAT.TEST_TBL01 ( ID NUMBER , NAME VARCHAR2(20) ); ALTER TABLE AYSHIBAT.TEST_TBL01 ADD CONSTRAINT TEST_TBL01_PK PRIMARY KEY(ID) USING INDEX;
3. OCI GG用 の DBユーザー作成&権限付与、初期化パラメータ設定
下記のマニュアルに従って OCI GG用 の DBユーザー を作成&権限付与します。
Oracle DatabaseでのOracle GoldenGateの使用 21c (21.3.0)
3.1.1.1 適切なユーザー権限の付与
https://docs.oracle.com/cd/F51462_01/oracle-db/establishing-oracle-goldengate-credentials.html#GUID-F9EBB989-E22F-4355-BE60-40F957B8515E
下記のパラメータも Source/Target の両方で TRUE に変更します。
Oracle Database データベース・リファレンス 19c
1.119 ENABLE_GOLDENGATE_REPLICATION
https://docs.oracle.com/cd/F19136_01/refrn/ENABLE_GOLDENGATE_REPLICATION.html#GUID-600FC071-1516-49B2-B3B3-C1C5430C5917
- Source側(DBCS 11.2.0.4) のDBユーザー作成、権限付与、パラメータ変更、サプリメンタル・ロギング設定
CREATE USER OGGAYSHIBAT IDENTIFIED BY xxxxxxxxxxxx DEFAULT TABLESPACE USERS TEMPORARY TABLESPACE TEMP; GRANT CREATE SESSION TO OGGAYSHIBAT; GRANT RESOURCE TO OGGAYSHIBAT; GRANT ALTER SYSTEM TO OGGAYSHIBAT; GRANT ALTER USER TO OGGAYSHIBAT; EXEC DBMS_GOLDENGATE_AUTH.GRANT_ADMIN_PRIVILEGE('OGGAYSHIBAT'); GRANT SELECT ANY DICTIONARY TO OGGAYSHIBAT; GRANT SELECT ANY TRANSACTION TO OGGAYSHIBAT; -- For User Object Privilege GRANT DBA TO OGGAYSHIBAT; ALTER SYSTEM SET enable_goldengate_replication = TRUE SCOPE=BOTH SID='*'; ALTER DATABASE ADD SUPPLEMENTAL LOG DATA;
- Target側(DBCS EE 19.14) のDBユーザー作成、権限付与、パラメータ変更
ALTER SESSION SET CONTAINER = PDB1; SHOW CON_NAME; CREATE USER OGGAYSHIBAT IDENTIFIED BY xxxxxxxxxxxx DEFAULT TABLESPACE USERS TEMPORARY TABLESPACE TEMP; GRANT CREATE SESSION TO OGGAYSHIBAT; GRANT RESOURCE TO OGGAYSHIBAT; GRANT ALTER SYSTEM TO OGGAYSHIBAT; GRANT ALTER USER TO OGGAYSHIBAT; EXEC DBMS_GOLDENGATE_AUTH.GRANT_ADMIN_PRIVILEGE('OGGAYSHIBAT', CONTAINER => 'PDB1'); GRANT LOCK ANY TABLE TO OGGAYSHIBAT; GRANT SELECT ANY DICTIONARY TO OGGAYSHIBAT; -- For User Object Privilege GRANT DBA TO OGGAYSHIBAT; -- These commands are required execute in CDB. ALTER SESSION SET CONTAINER = CDB$ROOT; ALTER SYSTEM SET enable_goldengate_replication = TRUE SCOPE=BOTH SID='*';
4. OCI GG Deployment の作成
OCI Console の 左上のハンバーガーボタン ⇒ Oracle Database ⇒ GoldenGate(右側)と遷移して Deployments の Create deployment をクリックします。
以下のように入力して行きます。
変数名 | 入力値 |
---|---|
Name | 任意の文字列を入力 |
Description | 任意の説明 |
Compartment | 任意のコンパートメントを選択 |
oCPU count | 必要なoCPU数 ※後から変更可能です。 |
Subnet | OCI GG をデプロイする VCN Subnet を選択 |
Choose a license type | ライセンス持ち込み以外は License included を選択します。 |
GoldenGate instance name | 任意の文字列を入力 |
Administrator usrename | 管理ユーザー名を入力 |
Administrator password | 管理ユーザーのパスワードを入力 |
デプロイが完了すると以下の画面のようになります。
5. データベースの登録
OCI Console の 左上のハンバーガーボタン ⇒ Oracle Database ⇒ GoldenGate(右側) と遷移して、Register Databases の Register Database をクリックします。
以下のように入力して行きます。Source と Target の両方を登録して下さい。
変数名 | 入力値 |
---|---|
Name | 任意の文字列 |
Alias Name | 任意の文字列ただし記号等の制限有り |
Description | 任意の説明を入力 |
Compartment | OCI GG を作成したコンパートメントを選択 |
Database Type | 今回は DB system database(Bare Metal, VM, Exadata) を選択します。 |
Database system in ~ | Source または Target の DBCS を選択します。 |
データベース(今回はDBCS)を選択すると他の変数は自動で入力されます。下記を追加入力&修正しています。 | 変数名 | 入力値 | |-|-| |Database connection string | DBサービス名部分を必要に応じて修正します。今回は Target DB の接続サービス名として pdb1 に修正しています。| |Database username | 上記 3. の手順で作成した OCI GG用 の DBユーザー名を入力します。| |Database user password | OCI GG用DBユーザー名 の パスワード を入力します。|
6. Deployment Console への接続 と データベース接続確認
OCI Console の Deploymentトップ画面から Deployment Console に接続します。
なお今回は Private Subnet に OCI GG Deployment を作成して手元のPCからの直接接続は不可だったため、Compute に SSH接続して SSHポートフォワードで Deployment Console に接続しています。
Deployment Console のトップ画面から 構成 ⇒ データベース(サブタブ) と遷移して、登録データベースのハンバーガーボタンを押して接続を確認します。
接続が成功すると「チェックポイント」「TRANDATA情報」「ハートビート」といった項目が表示されます。
7. スキーマ単位 または 表単位 のサプリメンタル・ロギング設定(Sourceのみ)
Deployment Console のトップ画面から 構成 ⇒ データベース(サブタブ) ⇒ Source のハンバーガーボタンと遷移して Sourceデータベースに接続します。
TRANDATA情報からスキーマ単位 または 表単位 のサプリメンタル・ロギングを設定します。
今回は スキーマAYSHIBAT に対してサプリメンタル・ロギングを設定します。
実行後にTRANDATA情報を検索すると、以下のようにTRANDATAが設定されていることを確認できます。
下記マニュアルも参照して下さい。
Oracle GoldenGate Microservicesアーキテクチャを使用してのステップ・バイ・ステップ・データ・レプリケーション
TRANDATAまたはSCHEMATRANDATA情報の有効化
https://docs.oracle.com/cd/F51462_01/ggmas/working-data-replications.html#GUID-C3B8B4EE-B13F-43BF-AE94-CFCB01B26F60
8. チェックポイント表の作成(Targetのみ)
チェックポイント表は Replicat がレプリケーションの進行状況を記録するための GoldenGate内部表 です。
Deployment Console のトップ画面から 構成 ⇒ データベース(サブタブ) ⇒ Source のハンバーガーボタンと遷移して Sourceデータベースに接続します。
チェックポイントからチェックポイント表の作成を発行します。
チェックポイント表の作成に成功すると、以下のように作成されたチェックポイント表が表示されます。
下記マニュアルも参照して下さい。
Oracle GoldenGate Microservicesアーキテクチャを使用してのステップ・バイ・ステップ・データ・レプリケーション
3.6 Replicatを作成する前に
https://docs.oracle.com/cd/F51462_01/ggmas/working-data-replications.html#GUID-2CE071E8-1B2F-4138-A01D-B5BC6A010062
9. ハートビート表の作成(Source と Target両方)
ハートビート表は GoldenGate の伝播状況、特にラグ(伝播遅延)を確認するのに極めて有益な情報となります。
17.4 自動ハートビート表を使用した監視
https://docs.oracle.com/cd/F51462_01/admin/monitoring-oracle-goldengate-processing.html#GUID-59E61274-BDDE-4D4B-9681-ED0BC39E9FCF
Deployment Console のトップ画面から 構成 ⇒ データベース(サブタブ) ⇒ Source のハンバーガーボタンと遷移して、まずは Sourceデータベースに接続します。
ハートビートからハートビート表の更新頻度、保存期間、パージ頻度を設定して発行ボタンをクリックします。
この時に Target Only のチェックは解除して下さい。
次に Deployment Console のトップ画面から 構成 ⇒ データベース(サブタブ) ⇒ Source のハンバーガーボタンと遷移して、まずは Targetデータベースに接続します。
ハートビートからハートビート表の保存期間、パージ頻度を設定して発行ボタンをクリックします。この時に Target Only のチェックは設定しておいて下さい。
10. Extract の作成
Deployment Console のトップ画面から Extract を作成します。Extract の +ボタン をクリックします。
統合Extract を選択して次へをクリックします。
以下を入力します。変更する部分のみをピックアップしています。
変数名 | 入力値 |
---|---|
プロセス名 | 任意の文字列ただし8文字まで |
トレイル名 | 2文字のPREFIXを入力 |
資格証明ドメイン | OracleGoldenGate を選択 |
資格証明別名 | Source DB の Alias を選択 |
共有 | 11.2.0.4 は統合ExtractのShareに対応していないため なし を選択 |
Extract のパラメータは以下のように設定します。GoldenGate は色々と多機能でここの設定も色々有るのですが、今回はシンプルに構成します。
EXTRACT EXT99 USERIDALIAS dbcsseogays DOMAIN OracleGoldenGate EXTTRAIL tr TABLE AYSHIBAT.*;
作成および実行をクリックして、正常に終了すると Extractプロセス が作成&起動します。
11. Replicat の作成
Deployment Console のトップ画面から Replicat を作成します。Replicat の +ボタン をクリックします。
Replicatプロセスの種別を選択します。色々あるのですが今回は 非統合Replicat を選択して次をクリックします。
以下を入力します。変更する部分のみをピックアップしています。
変数名 | 入力値 |
---|---|
プロセス名 | 任意の文字列ただし8文字まで |
資格証明ドメイン | OracleGoldenGate を選択 |
資格証明別名 | Target DB の Alias を選択 |
トレイル名 | Extract に設定した 2文字のPREFIXを入力 |
チェックポイント表 | 上記8. で作成したチェックポイント表を選択 |
Replicat のパラメータは以下のように設定します。やはり色々と設定はあるのですが、今回はここもシンプルに構成します。
REPLICAT REP99 USERIDALIAS dbcseeogays DOMAIN OracleGoldenGate MAP AYSHIBAT.*, TARGET AYSHIBAT.*;
作成および実行をクリックして、正常に終了すると Replicatプロセス が作成&起動します。
12. レプリケーションの確認(INSERT, UPDATE, DELETE, 統計, ラグ)
レプケーションを確認します。まず Source側 でデータを INSERT してみます。
SQL> SELECT INSTANCE_NAME FROM V$INSTANCE; INSTANCE_NAME ---------------- se01 SQL> INSERT INTO TEST_TBL01 SELECT LEVEL, 'REC' || LEVEL FROM DUAL CONNECT BY LEVEL <= 10; 10 rows created. SQL> COMMIT; Commit complete.
Target側 で対象テーブルを SELECT してみます。
SQL> SHOW CON_NAME; CON_NAME ------------------------------ PDB1 SQL> SELECT * FROM TEST_TBL01; ID NAME ---------- -------------------- 1 REC1 2 REC2 3 REC3 4 REC4 5 REC5 6 REC6 7 REC7 8 REC8 9 REC9 10 REC10 10 rows selected.
レプリケーションされてるやで!彡(^)(^)
UPDATE や DELETE も Source側 で実行してみます。
SQL> SELECT INSTANCE_NAME FROM V$INSTANCE; INSTANCE_NAME ---------------- se01 SQL> UPDATE TEST_TBL01 SET NAME = 'XXXXXXXX' WHERE ID = 10; 1 row updated. SQL> DELETE FROM TEST_TBL01 WHERE ID = 9; 1 row deleted. SQL> COMMIT; Commit complete.
SQL> SHOW CON_NAME; CON_NAME ------------------------------ PDB1 SQL> SELECT * FROM TEST_TBL01; ID NAME ---------- -------------------- 1 REC1 2 REC2 3 REC3 4 REC4 5 REC5 6 REC6 7 REC7 8 REC8 10 XXXXXXXX 9 rows selected.
UPDATE や DELETE もレプリケーションされていますやね彡(^)(^)
Deployment Console の 統計タブ からも件数レベルでの伝播状況を確認できます。
Deployment Console の ハートビート からはラグ(伝播遅延)の状況を確認可能です。
まとめ
OCI GoldenGate(OCI GG) で DBCS のデータをレプリケーションできたやで!彡(^)(^)
OCI GoldenGate は従量課金で利用可能なので、皆さんどんどん使ってくださいね。
Autonomous Database で SQLトレース を採取してみる。(Oracle Cloud Infrastructure)
いつの間にか Autonomous Database で SQLトレース を採取できるようになったので、下記のマニュアルを見ながらやってみます。
彡(゚)(゚)
Autonomous DatabaseでのSQLトレースの実行
https://docs.oracle.com/cd/E83857_01/paas/autonomous-database/adbsa/application-tracing.html#GUID-25A5160B-C72A-4897-9CC9-0BE23EA7EC01
1. Object Storage Bucket作成/IAMユーザー作成/ポリシー付与/Auth Token生成/クレデンシャル登録
詳細は本記事では省略します。下記記事を参照して下さい。
Object Storage にアップロードしたファイルを Autonomous Database の DATA_PUMP_DIR にコピーする。(Oracle Cloud Infrastructure)
https://qiita.com/ora_gonsuke777/items/3e8b63b1d878c7fe343e
2. DATABASE PROPERTY の設定(DEFAULT_LOGGING_BUCKET, DEFAULT_CREDENTIAL)
上記 1. で作成した Object Storage Bucket や クレデンシャル を DATABASE PROPERTY として登録します。
SET DEFINE OFF; ALTER DATABASE PROPERTY SET DEFAULT_LOGGING_BUCKET = 'https://objectstorage.us-phoenix-1.oraclecloud.com/n/xxxxxxxxxxxxxx/b/ayu-bucket1/o/'; ALTER DATABASE PROPERTY SET DEFAULT_CREDENTIAL = 'ADMIN.DEF_CRED_NAME2';
3. SQLトレースの採取
まずは IDENTIFIER名 と MODULE名 をセットします。これらはオプションですが SQLトレース の ファイル名 を特定するのに便利です。
BEGIN DBMS_SESSION.SET_IDENTIFIER('ayu_sqlt'); END; / BEGIN DBMS_APPLICATION_INFO.SET_MODULE('test1', null); END; /
SQLトレースをセットします。
ALTER SESSION SET SQL_TRACE = TRUE;
トレースをセットした状態で、性能を測定したい SQL を実行します。
ダミーデータをセットアップして下記SQLを実行してみました。
SELECT /*+ MONITOR NO_VECTOR_TRANSFORM */ /* AYSHIBAT */ B.ITEM_NAME , TRUNC(A.ORDER_DATE, 'DD') AS ORDER_DAILY , COUNT(*) FROM ORDER_TBL A , ITEM_TBL B WHERE A.ITEM_NO = B.ITEM_NO -- AND B.ITEM_NO BETWEEN 1 AND 100 AND TO_CHAR(B.REGIST_DATE, 'YYYYMMDD') = '20120801' GROUP BY B.ITEM_NAME, TRUNC(A.ORDER_DATE, 'DD') ORDER BY 1;
SQLトレースを解除します。
ALTER SESSION SET SQL_TRACE = FALSE;
4. Object Storage Bucket の SQLトレースの確認とダウンロード
Object Storage の Bucket に出力された SQLトレース を確認してダウンロードします。
5. SQLトレース の tkprof による整形と確認
Autonomous Database は OS にはログインできないため、
SQLトレースを整形する場合は別環境で実施する必要があります。
対応するバージョンの tkprofコマンドがセットアップされていれば環境は問わないのですが、
今回は下記の VirtualBox環境 で実施してみました。
OTN の VirtualBoxイメージ で Oracle DB 19c環境 を 楽々構築
https://qiita.com/ora_gonsuke777/items/b41f37637e59319796b4
採取した SQLトレース を VirtualBox の 仮想OS にアップロードして、tkprofコマンド で整形して確認します。
export ORACLE_HOME=/u01/app/oracle/product/version/db_1 export PATH=${PATH}:${ORACLE_HOME}/bin tkprof sqltrace_ayu_sqlt_test1_sqltrace_21569_30852.trc sqltrace_ayu_sqlt_test1_sqltrace_21569_30852.trc.txt view sqltrace_ayu_sqlt_test1_sqltrace_21569_30852.trc.txt
下記のように整形後のSQLトレースを確認できました。
: SELECT /*+ MONITOR NO_VECTOR_TRANSFORM */ /* AYSHIBAT */ B.ITEM_NAME , TRUNC(A.ORDER_DATE, 'DD') AS ORDER_DAILY , COUNT(*) FROM ORDER_TBL A , ITEM_TBL B WHERE A.ITEM_NO = B.ITEM_NO -- AND B.ITEM_NO BETWEEN 1 AND 100 AND TO_CHAR(B.REGIST_DATE, 'YYYYMMDD') = '20120801' GROUP BY B.ITEM_NAME, TRUNC(A.ORDER_DATE, 'DD') ORDER BY 1 call count cpu elapsed disk query current rows ------- ------ -------- ---------- ---------- ---------- ---------- ---------- Parse 1 0.00 0.00 0 0 0 0 Execute 1 0.00 0.00 0 0 0 0 Fetch 19 0.48 0.49 0 9177 0 261 ------- ------ -------- ---------- ---------- ---------- ---------- ---------- total 21 0.48 0.49 0 9177 0 261 Misses in library cache during parse: 0 Optimizer mode: ALL_ROWS Parsing user id: 157 Number of plan statistics captured: 1 Rows (1st) Rows (avg) Rows (max) Row Source Operation ---------- ---------- ---------- --------------------------------------------------- 261 261 261 SORT GROUP BY (cr=9177 pr=0 pw=0 time=496578 us starts=1 cost=30 size=26 card=1) 26000 26000 26000 HASH JOIN (cr=9177 pr=0 pw=0 time=490431 us starts=1 cost=29 size=26 card=1) 2600000 2600000 2600000 JOIN FILTER CREATE :BF0000 (cr=9086 pr=0 pw=0 time=148913 us starts=1 cost=3 size=286 card=26) 2600000 2600000 2600000 TABLE ACCESS STORAGE FULL ORDER_TBL (cr=9086 pr=0 pw=0 time=69464 us starts=1 cost=3 size=286 card=26) 10 10 10 JOIN FILTER USE :BF0000 (cr=91 pr=0 pw=0 time=8325 us starts=1 cost=26 size=4500 card=300) 10 10 10 TABLE ACCESS STORAGE FULL ITEM_TBL (cr=91 pr=0 pw=0 time=7935 us starts=1 cost=26 size=4500 card=300)
一番時間が掛かっていそうなのは、下位Operattion との time値 のギャップが大きい HASH JOIN の部分ですかね~~。
彡(゚)(゚)
6. まとめ
Autonomous Database で SQLトレース を採取して、他環境で整形できるのを確認できました。
現時点(2022/2/28)で待機イベントやバインド変数が取得できないのが残念ですが、今後に期待や!彡(^)(^)