RDBMSとグラフ
最後に、グラフDBMSに求められている機能や性能がRDBMSを用いて実現できるかどうか考えてみます。度々取り上げているDBMSの3階層の分離の考え方によれば、RDBMSの実装を用いながらグラフDBMSのデータモデルやクエリ言語に対応するといったこともできるはずです。なんでもかんでもRDBMSに押し込めなくてもいいではないかという意見ももっともですが、新しい機能を統合するための階層デザインを実現してきたのがDBMSですから、試してみる価値はあるはずです。
実際のところ、上で紹介したようなユースケースでは、データソースとなるトランザクションが表で管理されている、複数データソースの統合が必要である、高いセキュリティ要件が求められる、といった障壁のためにグラフDBMSの導入が進みませんでした。今後、ビジネス・クリティカルなシステムでグラフのデータモデルやクエリを採用したい場合には、表データとの管理の一元化や、現行RDBMSと同レベルの信頼性や可用性、といった課題は避けて通れません。
もしもRDBMSを拡張してグラフDBMSと同様の機能と性能を提供できるのであれば上記のような課題を解決できるはずです。では、それは可能なのかどうか、3つの階層それぞれについて見ていきたいと思います。
RDBMSを拡張するという発想はOracle Graphの他にも、PostgreSQLの拡張であるApache AGEなどで採用されています。Apache AGEは2022年5月にApache Software Foundationのトップレベルプロジェクトになりました。
データモデルは変換できる
まず、データモデルについて考えてみます。グラフと表との大きな違いは、グラフには「ノードとエッジ」の2種類の構造があることでした。グラフの世界では、なにがノードでなにがエッジかが決まっています。この表現方法を理解するために、一例として、銀行送金を表現している次の2つの表を考えます。
ここで、ACCOUNT表はノード、TRANSFER表はエッジと捉えることができます。これは私たちが直感的に考えてわかりやすい対応ですが、そういった人間の「直感」がなくても、TRANSFER表が始点と終点にあたる銀行口座に対してACCOUNT表への参照(外部参照制約が付与されているかもしれません)を持っていることから、グラフの構造を推測することもできそうです。
次の構文は、そのような表とグラフの対応を表現したものです。GRAPH1というグラフを作成していて、その際、ACCOUNT表はVERTEX TABLESのひとつ、TRANSFER表はEDGE TABLESのひとつである、としています。
CREATE PROPERTY GRAPH graph1
VERTEX TABLES (
account
KEY (acc_id)
LABEL account
PROPERTIES (acc_id, cst_id)
)
EDGE TABLES (
transfer
KEY (txn_id)
SOURCE KEY(acc_id_src) REFERENCES account
DESTINATION KEY(acc_id_dst) REFERENCES account
LABEL transferred_to
PROPERTIES (txn_id, date, amount)
)
上の構文が実体として何を作るかはひとまず置いておいて、抽象モデルとしてのグラフはこれで作ることができたということにしておきましょう。このグラフを可視化するとすれば以下の図のようになります。
Oracle Graphの場合には、グラフの実体として、データを新たな表に格納し直したもの、既存の表の上にビューを作成したもの、メモリ上に展開したもの、の3つの選択肢があります。
クエリ言語は翻訳できる
次にクエリ言語です。グラフのパターンマッチングを使ったクエリを、上の2つの表に対して実行するのであれば、SQLに翻訳してしまえばあとはRDBMSの既存のメカニズムが使えます。
例えば、次のPGQLクエリは指定したノードから3ホップ先まで辿った先のノードを取得するものです。
SELECT a1.acc_id AS a1, a2.acc_id AS a2 FROM MATCH (a1:account)->()->()->(a2:account) ON graph1 WHERE a1.acc_id = 10;
このクエリは以下のようなSQLに翻訳することができます。
SELECT a1.acc_id AS a1, a4.acc_id AS a4
FROM account a1, account a4,
transaction t1, transaction t2, transaction t3
WHERE a1.acc_id = 10
AND a1.acc_id = t1.acc_id_src
AND t1.acc_id_dst = t2.acc_id_src
AND t2.acc_id_dst = t3.acc_id_src
AND t3.acc_id_dst = a4.acc_id
SQLにして見てみると表の結合が繰り返されているので、簡潔なPGQLクエリを使った方がなんとなく性能が良さそうな気もするかもしれません。しかしながら、実際の性能はクエリで決まるわけではなく、実装とそれに伴う実行モデルに依存しています。
実行モデルはDBMSが選択する
先のクエリの場合には、もしもデータをグラフとしてメモリに保持しておくことができれば「辿る」処理を効率化するような実行モデルを選択することができます。その一方で、PGQLクエリを使っていても、集計が必要な場合など、表として格納されていた方がより効率的な実行モデルを作成できることも多々あります。
例えば、次のPGQLクエリは銀行送金の合計額が多いアカウントのペアを求めています。
SELECT a1.acc_id AS a1, a2.acc_id AS a2, SUM(t.amount) AS total FROM MATCH (a1:account)-[t:transferred_to]->(a2:account) ON graph1 GROUP BY a1.acc_id, a2.acc_id ORDER BY total DESC LIMIT 5
SQLに翻訳すると以下のようになります。
SELECT a1.acc_id AS a1, a2.acc_id AS a2, SUM(t.amount) AS total FROM account a1, account a2, transaction t WHERE t.acc_id_src = a1.acc_id AND t.acc_id_dst = a2.acc_id GROUP BY a1.acc_id, a2.acc_id ORDER BY total DESC FETCH FIRST 5 ROWS ONLY;
このクエリでは「辿る」処理が少ない上に、全件走査が必要なので、表のスキャンが効率的です。PGQLクエリを使っていても「辿る」処理を用いた実行モデルが常に最適なわけではないことは明らかです。このように性質の異なるクエリに対して最適な実行モデルを選択できることこそがDBMSの役割であるはずです。
RDBMSとグラフの展望
以上のように、DBMSの3階層モデルに従えば、RDBMSであってもグラフDBMSのクエリ言語とデータモデルをサポートすることができます。それが実現すれば、アプリケーションが実装を意識せずにクエリを発行していても、表とグラフの実装を使い分けて効率的な実行モデルで結果を取得する、そんなDBMSが生まれる可能性があります。
データの格納先として表とグラフ(メモリ上のCSRを使った圧縮方式)の両方のネイティブ実装を提供しているのがOracle Graphですが、現時点ではまだそれら両方を用いた実行パスを自動的に選択することはできません。
SQLへのグラフパターン記法の導入は既にSQL2020のSQL/PGQという仕様に盛り込まれており、今後はこの仕様が主たるRDBMSに導入され、グラフパターン記法で書かれたクエリを高速に実行するための実装の発展が期待されます。グラフ構造をキャッシュとして利用したり、それを他の索引やパーティショニングと組み合わせたりと、様々なアイディアが試行されるはずです。
まとめ
今回はグラフというデータモデルに着目して、DBMSの3つの階層それぞれにおける独自性や優位点、このデータモデルが活用できる主なユースケース、さらにRDBMSに統合していく試み、について紹介しました。
グラフは既に多くの開発者に認知されていて、各インダストリにおけるユースケースもわかってきています。その一方で、エンタープライズ・システムのデータをグラフ特化型DBMSのみを用いて管理することは難しく、他のDBMSとのデータ連携もしくは統合といった課題にたびたび直面しています。
このシリーズで冒頭から議論しているDBMSの3階層の分離という考え方に立ち戻って考えてみると、異なるデータモデルを利用しつつより効率的な実行モデルを選択できることもDBMSの重要な役割であることがわかります。マルチモデルDBMSの成熟が、今まで十分に使われてこなかったグラフデータの実利用を後押しすると期待しています。
