サイト内の現在位置を表示しています。

Oracle Database 23ai新機能「JSON Relational Duality」の機能・性能検証レポート

このレポートでは、Oracle Database 23aiの新機能「JSON Relational Duality」について、前半では技術背景と機能概要を説明し、その機能活用イメージを紹介します。後半ではNECで実施した機能/性能検証の結果を報告します。

目次

1.技術背景

・データベースへのJSONデータの格納方式
従来、JSONデータをデータベースに格納するためには、JSONデータをドキュメントとして取り扱い、単一列に丸ごと格納する方式が一般的でした(MongoDBなどのドキュメント指向データベース、Oracle21cのJSON型、PostgreSQLのjson, jsonb型など)。

この方式ではJSONデータの各要素をリレーショナルに管理することはできず、複数のJSONデータが共通部分を持つ場合でも、それぞれ別々に格納し管理していく必要があります。

そのため、各データの一貫性が確保されない上に、データ更新やデータ保存効率の低下が問題となります。

・JSONデータの同時更新制御について
データベースに格納されたJSONデータを複数セッションにて同時更新する際は、上書きによる消失の可能性を考慮する必要があります。
例として以下図では、Aが更新したJSONデータをBが上書き更新してしまい、Aのデータ更新が消失しています。

そのため、複数セッションにて同時にJSONデータを更新する場合は、アプリケーション側で同時更新制御処理を別途実装するなどの工夫が必要になります。

2.機能概要

23ai新機能「JSON Relational Duality」は、リレーショナル表でJSONデータを管理することが可能となる機能です。
リレーショナル表に対して作成したJSON Relational Duality Viewにより、JSONデータの構築やJSONデータの分解をデータベース側で自動的に実施します。

- JSONデータを自動分解し、リレーショナル表形式でデータを格納/管理可能
- リレーショナル表からJSONデータを自動構築し、JSONデータ形式で参照が可能
- SELECT, INSERT以外の基本操作(UPDATE/DELETE)もJSONデータ形式で実施することが可能
- SQLだけでなく、REST APIを通じたデータのやり取りが可能

また、同一のリレーショナル表に対して複数の異なるJSON Relational Duality Viewを作成することが可能であり、アプリケーションの用途に応じたJSONデータを構築することが可能です。

また、JSON Relational Dualityでは、etagを利用して更新内容の上書きによる消失を防止します。
etagとは、データベースに格納されているJSONデータの各要素から算出されるハッシュ値であり、このetagの値を比較することで、別セッションによるデータ更新の有無を判断します。

- 更新JSONデータに付与されているetagがデータベース内のetagと同じである場合、別セッションによる更新が発生していないと判断し、更新処理を許可
- etagはデータベース内のJSONデータの各要素から算出されるため、データベース内のJSONデータが更新されるとデータベース内のetagも更新される
- 更新JSONデータに付与されているetagがデータベース内のetagと異なる場合、別セッションによる更新が発生したと判断し、更新を拒否

上述した機能により、JSON Relational Dualityでは以下のようなメリットを享受することが可能です。
- JSONデータをリレーショナル表で管理することで、JSONデータの保存や更新効率が向上、データの一貫性を確保可能
- REST APIによるアクセスや、アプリケーションに応じたJSONデータの構築が可能であり、ユースケースへの柔軟性が高い
- etagによる同時更新制御がデータベース側にて自動実施され、別途アプリケーション側で同時更新制御について実装する必要がない

3.機能活用イメージ

近年、JSONはその可読性やプログラミング言語との親和性の高さから、アプリケーション間のデータ連携やAPIを用いたデータ取得時など、様々な場面で活用されるようになりました。
そんな中、Oracle Database 23aiの新機能「JSON Relational Duality」では何ができるようになるのか、その活用イメージを受発注アプリケーションを構成する場合を例としてご紹介します。

<従来手法の場合>
従来のアプリケーション構成として、データベースにJSONデータを格納せず、データをリレーショナル表で管理する場合のアプリケーション構成例は以下のようになります。

従来手法によるアプリケーション構成例

受注アプリケーションは内部でJSONデータを利用していますが、データベースとのやり取りのためにデータを変換し、SQLを生成する必要があります。
また、請求アプリや受注履歴アプリといったバックエンドのアプリケーションにおいても、データベースとのやり取りするためだけにSQLを生成、その後使いやすいデータ形式に変換することが必要です。
このようにアプリケーション側でデータ変換処理を実装する必要があり、アプリケーション開発者への負担が大きいという問題があります。

また、その他の既存手法として、データベースにJSONデータを直接格納し、REST API等でやり取りする場合のアプリケーション構成例は以下のようになります。

従来手法によるアプリケーション構成例(REST API)

この場合、各種アプリケーションのデータ形式をJSONで統一することが可能であり、データ変換やSQLの生成等は必要なく、アプリケーション開発者への負担を軽減することが可能です。
しかし、データはリレーショナルに管理されていないため、各種アプリケーションごとにJSONデータを格納する必要があり、データ一貫性の問題やデータの保存/更新効率低下の問題が発生します。
また、JSONデータの同時更新が発生する可能性がある場合は、アプリケーション側で同時更新制御を実装する必要があります。

<JSON Relational Dualityの場合>
JSON Relational Dualityを用いたアプリケーション構成例は次のようになります。

JSON Relational Dualityによるアプリケーション構成例

JSON Relational Dualityでは、各種アプリケーションにてデータ変換やSQL生成が不要となることに加え、データをリレーショナルに管理することが可能です。
すなわちデータ一貫性の問題や、データ保存/更新効率の問題に悩まされることはありません。
また、各種アプリケーションごとに異なるJSON Relational Duality Viewを作成することで、それぞれのアプリケーションで最適なJSONデータ構造を利用することが可能です。
また、etagによる同時更新制御がデータベース側で実施されるため、アプリケーション側で別途実装する必要もありません。

以上より、JSON Relational Dualityを活用することで、アプリケーション開発者への負担を大きく減らし、開発コストの低下、ビジネスサイクルの高速化につなげることが可能です。


以降では、NECで実施したJSON Relational Dualityの機能検証/性能検証の結果をご紹介いたします。

4.機能検証

・JSON Relational Duality Viewの作成
JSON Relational Duality Viewを作成する際は、SQLもしくはGraphQLが利用可能です。
リレーショナル表からJSONデータを構築するレシピを作成するイメージで、JSON Relational Duality Viewを作成します。

<SQLJSON Relational Duality Viewを作成>

CREATE JSON RELATIONAL DUALITY VIEW EMP_JRD AS
  SELECT JSON {
    'EmpID' : emp.EmpID,
    'Name' : emp.Name,
    'Mail' : emp.Mail,
    'Dept' : (
      SELECT JSON {
        'DeptID' : dept.DeptID,
        'DeptName' : dept.Name
      }
      FROM DEPARTMENT_TBL dept
      WHERE dept.DeptID = emp.DeptID
    )
  }
  FROM EMPLOYEE_TBL emp
;

<GraphQLでJSON Relational Duality Viewを作成>

CREATE JSON RELATIONAL DUALITY VIEW EMP_JRD AS
  EMPLOYEE_TBL {
    EmpID : EmpID
    Name : Name
    Mail : Mail
    Dept : DEPARTMENT_TBL {
      DeptID : DeptID
      DeptName : Name
    }
  }
;

また、JSON Relational Duality Viewに対して許可する操作を指定することが可能です。SQLの場合はWITH句を、GraphQLの場合は@句を記述することで指定します。テーブル単位、もしくは要素単位での指定が可能です。なお、デフォルトでは参照操作のみ許可されています。

< SQL
JSON Relational Duality Viewを作成(許可する操作を指定)>

CREATE JSON RELATIONAL DUALITY VIEW EMP_JRD AS
  SELECT JSON {
    'EmpID' : emp.EmpID,
    'Name' : emp.Name,
    'Mail' : emp.Mail,
    'Dept' : (
      SELECT JSON {
        'DeptID' : dept.DeptID,
        'DeptName' : dept.Name WITH UPDATE
      }
      FROM DEPARTMENT_TBL dept
      WHERE dept.DeptID = emp.DeptID
    )
  }
  FROM EMPLOYEE_TBL emp WITH INSERT UPDATE DELETE
;

<GraphQLでJSON Relational Duality Viewを作成(許可する操作を指定)>

CREATE JSON RELATIONAL DUALITY VIEW EMP_JRD AS
  EMPLOYEE_TBL @INSERT @UPDATE @DELETE{
    EmpID : EmpID
    Name : Name
    Mail : Mail
    Dept : DEPARTMENT_TBL {
      DeptID : DeptID
      DeptName : Name @UPDATE
    }
  }
;

・JSONデータの参照
SQLもしくはREST APIを用いて、JSONデータを参照することが可能です。
JSON Relational Duality Viewに対してSQLの場合はSELECT、REST APIの場合はGETメソッドを実行することで、JSONデータを構築し参照します。
このとき、同時更新制御に用いるデータベース内の"etag"の値も、"_metadata"フィールドの要素としてJSONデータに付与される形で参照されます。

<SQLでJSONデータを参照>

SELECT json_serialize(data PRETTY) json_data
  FROM EMP_JRD e
  WHERE e.data.EmpID = 1001;
 
JSON_DATA
--------------------------------------------------------------------------------
{
  "_metadata" :
  {
    "etag" : "0236F1378A762564269666678E5BCE60",
    "asof" : "0000000000A2C4E8"
  },
  "EmpID" : 1001,
  "Name" : "YAMADA TARO",
  "Mail" : "yamada@xxx.yyy",
  "Dept" :
  {
    "DeptID" : "A01",
    "DeptName" : "Planning_A"
  }
}

<REST APIでJSONデータを参照>

curl -X GET -H "Content-Type:application/json"
  --url http://localhost:8080/ords/juser/EMP_JRD/1001
 
{
 "EmpID" : 1001,
 "Name" : "YAMADA TARO",
 "Mail" : "yamada@xxx.yyy",
 "Dept" : {"DeptID" : "A01", "DeptName" : "Planning_A"},
 "_metadata": {"etag": "0236F1378A762564269666678E5BCE60", "asof": "0000000021DD1C05"},
 "links":[
   {"rel": "self", "href": "http://localhost:8080/ords/juser/EMP_JRD/1001"},
   {"rel": "describedby", "href": "http://localhost:8080/ords/juser/metadata-catalog/EMP_JRD/item"},
   {"rel": "collection", "href": "http://localhost:8080/ords/juser/EMP_JRD/"}
 ]
}

・JSONデータの格納
SQLもしくはREST APIを用いて、JSONデータを格納することが可能です。
JSON Relational Duality Viewに対してSQLの場合はINSERT、REST APIの場合はPOSTメソッドを実行することで、JSONデータを分解しリレーショナル表に格納します。

<SQLでJSONデータを格納>

INSERT INTO EMP_JRD VALUES(
  '{
    "EmpID" : 1002,
    "Name" : "SUZUKI ICHIRO",
    "Mail" : "suzuki@xxx.yyy",
    "Dept" : {
      "DeptID" : "A01",
      "DeptName" : "Planning_A"
    }
  }'
);
 
1行が作成されました。

<REST APIJSONデータを格納>

curl -X POST -H "Content-Type:application/json"
  --url http://localhost:8080/ords/juser/EMP_JRD/

  --data
    '{
      "EmpID" : 1002,
      "Name" : "SUZUKI ICHIRO",
      "Mail" : "suzuki@xxx.yyy",
      "Dept" : {
        "DeptID" : "A01",
        "DeptName" : "Planning_A"
      }
    }'
 
{
 "EmpID" : 1002,
 "Name" : "SUZUKI ICHIRO",
 "Mail" : "suzuki@xxx.yyy",
 ...
  {"rel":"describedby","href":"http://localhost:8080/ords/juser/metadata-catalog/EMP_JRD/item"},
  {"rel":"collection","href":"http://localhost:8080/ords/juser/EMP_JRD/"}
 ]
}

・JSONデータの更新
SQLもしくはREST APIを用いて、JSONデータを更新することが可能です。
JSON Relational Duality Viewに対してSQLの場合はUPDATE、REST APIの場合はPUTメソッドを実行することで、リレーショナル表に格納されているデータを更新します。
ただし、JSONデータの更新時は前述したetagによる同時更新制御処理が行われるため、"_metadata"フィールドの要素として"etag"の値をJSONデータに付与する必要があります。

<SQLでJSONデータを更新>

UPDATE EMP_JRD e SET data = (
  '{
    "_metadata" : {
      "etag" : "0236F1378A762564269666678E5BCE60"
    },
    "EmpID" : 1001,
    "Name" : "YAMADA TARO",
    "Mail" : "yamada2@xxx.yyy",
    "Dept" : {
      "DeptID" : "A01",
      "DeptName" : "Planning_A"
    }
  }'
)
WHERE e.data.EmpID = 1001;
 
1行が更新されました。

<REST APIでJSONデータを更新>

curl -X PUT -H "Content-Type:application/json"
  --url http://localhost:8080/ords/juser/EMP_JRD/1001
  --data
    '{
      "_metadata" : {
        "etag" : "0236F1378A762564269666678E5BCE60"
      },
      "EmpID" : 1001,
      "Name" : "YAMADA TARO",
      "Mail" : "yamada2@xxx.yyy",
      "Dept" : {
        "DeptID" : "A01",
        "DeptName" : "Planning_A"
      }
    }'
 
{
 "EmpID" : 1001,
 "Name" : "YAMADA TARO",
 "Mail" : "yamada2@xxx.yyy",
 ...
  {"rel":"describedby","href":"http://localhost:8080/ords/juser/metadata-catalog/EMP_JRD/item"},
  {"rel":"collection","href":"http://localhost:8080/ords/juser/EMP_JRD/"}
 ]
}

もし、JSONデータに付与した"etag"の値が、データベース内の"etag"の値と異なる場合、UPDATE処理は拒否されエラーが返却されます。

<SQLJSONデータを更新(etag不一致エラー)>

UPDATE EMP_JRD e SET data = (
  '{
    "_metadata" : {
      "etag" : "11111111111111111111111111111111"
    },
    "EmpID" : 1001,
    "Name" : "YAMADA TARO",
    "Mail" : "yamada3@xxx.yyy",
    "Dept" : {
      "DeptID" : "A01",
      "DeptName" : "Planning_A"
    }
  }'
)
WHERE e.data.EmpID = 1001;
 
1でエラーが発生しました。:
ORA-42699: Cannot update JSON Relational Duality View 'EMP_JRD': The ETAG of document with ID 'FB03C20B0200' in the database did not match the ETAG passed in.

< REST APIJSONデータを更新(etag不一致エラー)>

curl -X PUT -H "Content-Type:application/json"
  --url http://localhost:8080/ords/juser/EMP_JRD/1001
  --data
    '{
      "_metadata" : {
        "etag" : "11111111111111111111111111111111"
      },
      "EmpID" : 1001,
      "Name" : "YAMADA TARO",
      "Mail" : "yamada3@xxx.yyy",
      "Dept" : {
        "DeptID" : "A01",
        "DeptName" : "Planning_A"
      }
    }'
 
{
    "code": "PredconditionFailed",
    "message": "Predcondition Failed",
    "type": "tag:oracle.com,2020:error/PredconditionFailed",
    "instance": "tag:oracle.com,2020:ecid/gkemctFie4dedNbkbzqZZA"
}

5.性能検証(RESTを用いたUPDATE)

<目的>
JSON Relational Dualityではデータ形式変換(JSONデータの構築・分解)処理および同時更新制御処理が発生します。
上記処理が発生した上で、JSONデータの更新効率向上が確認できるのかを検証します。

<検証環境>
Oracle REST Data Servicesを使用し、SQLではなくHTTPメソッドでデータベースにアクセスします。

<検証方法>
従来のJSONデータ管理方式である21c JSON型列と23ai JSON Relational Dualityにおいて、JSONデータの更新にかかる実行時間を比較します。

<検証条件>

  • Oracle REST Data Services(ORDS)を使用し、SQLではなくHTTPメソッドでデータベースにアクセスします
  • 検証ではJSONデータに含まれる1つの要素を更新します

  • 更新する要素は複数のJSONデータがもつ共通部分であり、1回の更新につき計100件のJSONデータを更新する必要があります
  • 複数セッションによる同時更新は実施せず、シリアル実行とします(APサーバー側で同時更新制御の実装なし)

<実行クエリ例>
・従来のJSON管理方式の場合

# JSONデータの取得
curl -X GET -H "Content-Type:application/json" -o <JSONファイル名>
  --url http://localhost:8080/ords/juser/<Table A>/<Primary Keyの値>
curl -X GET -H "Content-Type:application/json" -o <JSONファイル名>
  --url http://localhost:8080/ords/juser/<Table B>/?q={"<フィールド名>":{"$eq":<>}}

 
# JSONデータの更新
curl -X PUT -H "Content-Type:application/json" --data-binary <JSONファイル名>
  --url http://localhost:8080/ords/juser/<Table A>/<Primary Keyの値>
curl -X PUT -H "Content-Type:application/json" --data-binary <JSONファイル名>
  --url http://localhost:8080/ords/juser/<Table B>/<Primary Keyの値>

※従来のJSON管理方式の場合、関連する複数のJSONデータを取得/更新が必要となります。

JSON Relational Dualityの場合

# JSONデータの取得
curl -X GET -H "Content-Type:application/json" -o <ファイル名>
  --url http://localhost:8080/ords/juser/<JSON Relational Duality View>/<Primary Keyの値>
 
# JSONデータの更新
curl -X PUT -H "Content-Type:application/json" --data-binary <ファイル名>
  --url http://localhost:8080/ords/juser/<JSON Relational Duality View名>/<Primary Keyの値>

<検証結果>
JSONデータ更新にかかる実行時間

 - JSON Relational Dualityは、従来のJSON管理方式と比べてデータ更新にかかる実行時間が短縮

JSON Relational Dualityでは、データ形式変換および同時更新制御処理が発生するが、それを踏まえた上でJSONデータの更新効率が向上することが確認できました。

6.性能検証(SQLを用いたINSERT)

<目的>
テーブルに対してJSON Relational Duality Viewを作成することで、テーブル側に対して性能影響があるのかを確認します。
JSONデータをリレーショナル表形式に自動変換する処理にかかるコスト(実行時間)はどの程度かを確認します。

<検証方法>
以下3つのパターンにおいて、データの挿入(INSERT)にかかる実行時間を測定します。
(A)テーブル(JSON Relational Duality View未作成)に対するINSERT
(B)テーブル(JSON Relational Duality View作成済み)に対するINSERT
(C)JSON Relational Duality Viewに対するINSERT

- (A)(B)を比較することで、JSON Relational Duality Viewの作成有無が、テーブル操作(INSERT)の性能に影響を与えるのか検証します
 - (A)(C)を比較することで、JSON Relational Duality Viewによるデータ形式変換処理にかかる実行時間を検証します

<検証条件>
INSERT対象がJSON Relational Dualityの場合、INSERT文に記述するJSONデータはデータベース側でレコード形式に自動変換されます(=データベース側で複数のINSERT文に分解されます)
INSERT対象がテーブルの場合、INSERT文に記述するレコードデータは事前にアプリケーション側でJSONデータから分解されます(=アプリケーション側で複数のINSERT文に分解しています)

<実行クエリ例>
(A)テーブル および (B)テーブルの場合

# レコードのINSERT
INSERT INTO <TABLE 1> VALUES(<1>, ... ,<6>);
INSERT INTO <TABLE 2> VALUES(<7>, ... ,<16>);
INSERT INTO <TABLE 2名> VALUES(<17>, ... ,<26>);

(C) JSON Relational Duality Viewの場合

# JSONデータのINSERT
INSERT INTO <JSON Relational Duality View> VALUES('
 {
   "<フィールド1>": <1>,
   "<フィールド2>": <2>,
   ... ,
   "<フィールド26>": <26>
 }
');

<検証結果>
JSONデータINSERTにかかる実行時間

 - (A)と(B)を比較すると、実行時間は同等。
  → JSON Relational Duality Viewの有無により、テーブル操作(INSERT)への性能影響はなし。
 - (A)と(C)を比較すると、(C)の実行時間がやや短縮。
  → JSON Relational Duality Viewによるデータ形式変換処理にかかる実行時間は軽微。
  → アプリケーション側でJSONデータを分解し、複数INSERT文を発行するより、データベース側(JSON Relational Duality View)で複数INSERT文に分解する方が効率的になりました。

7.まとめ

JSON Relational Dualityは、主にJSONデータとリレーショナル表の相互変換やetagによる同時更新制御を実施する機能です。これにより、JSONデータの保存や更新効率の向上、データ一貫性の確保、アプリケーション開発者の負担低減などのメリットを享受することが可能です。

JSON Relational DualityについてNECで検証した結果、データ形式変換および同時更新制御処理を踏まえた上で、JSONデータの更新効率が向上することを確認しました。また、JSON Relational Duality Viewの有無により、テーブル操作への性能影響はないことを確認しました。アプリケーション側でJSONデータを分解し、複数INSERT文を発行するより、JSON Relational Duality Viewにより複数INSERT文に分解する方が効率的になることを検証しました。