QLDB

【AWS】QLDBへのPartiQLクエリ実行とパフォーマンスの最適化について

どうも、AWS勉強中のとがみんです。

改竄がされていないことを証明したいデータを管理するために、台帳データベースを実際に使っていくにあたって、台帳に保存されたデータを集計したいと考えた時に、検索のパフォーマンスについて気になったので調べてみることにしました。

QLDBのデータ構造と、SQLlikeに実行できるPartiQLについての整理とクエリ最適化について整理していきます。

QLDBのデータ構造とPartiQL

QLDBのデータ構造

Amazon QLDBは、データレコードを「ドキュメント」として保存され、それはAmazon Ionというstruct型のオブジェクトです。そのオブジェクトがテーブルに挿入されます。

Amazon Ionの具体例は以下のようなものです。

QLDBドキュメントは「名前」と「値」のペアで構成され、struct型のオブジェクトは{}で表現されlist型は[]で表現されます。

PartiQL

QLDBに格納されたデータは、PartiQLというSQLを拡張したクエリ言語を活用してデータリソースへのアクセスを行うことができます。

PartiQLは、リレーショナル形式のデータベースだけでなく、Key Value Storeや、JSON形式のNpSQLデータベース、CSVファイルなど、様々なデータソースに対して、同じ構文で問い合わせることができます。

QLDBで管理されているIon構造のデータに対してもPartiQLというクエリ言語を活用してSQLライクにデータアクセスできます。

参考:QLDB documents

PartiQLとIon型のマッピング

Ionのstruct型は、SQLのタプル型と同等のものです。
タプルとはリレーショナルデータベースにおいて、テーブルの行にあたるものです。

QLDBに下記のデータ構造のドキュメントを挿入し、クエリとその実行結果について確認していきます。

SELECT * FROM PERSON WHERE ID = ‘12345’

SELECT文で挿入したドキュメントを確認しました。
Amazon Ion型のJSONは上記のようなテーブルとして表現されます。

Hobbyにリストとして要素が複数入っているので、その中身を参照してみます。

SELECT Hobby[0] FROM PERSON WHERE ID = ‘12345’

さらにリストの一つの要素の中の値を取得します。

SELECT Hobby[0].name FROM PERSON WHERE ID = ‘12345’

このようにAmazon Ion型のデータとSQLライクなPartiQLがマッピングされます。

QLDBへのクエリパフォーマンス

Amazon QLDBは、オンライントランザクション処理ワークロードのニーズに対応することを目的としています。そのため、SQLのようなクエリ機能をサポートしているが、特定のクエリパターンに最適化されています。

特定のクエリパターン以外だと、クエリはテーブル全体のスキャンを呼び出してしまい、クエリのレイテンシー、トランザクションのタイムアウト、同時実行による競合など、パフォーマンス上の問題が発生してしまいます。

そのため最適なクエリパターンに沿ってクエリ実行することが推奨されています。

オンライントランザクション処理:処理要求があるとホストコンピューターが即座に処理応答する方式。
レイテンシー:要求されたリソースが目的値に到達するまでにかかるネットワーク時間。

最適なクエリパターン例

パフォーマンス上最適な検索を行うためには、カーディナリティが高いフィールドにインデックスを作成し、そのフィールド、またはドキュメントIDでフィルタリングすることが推奨されています。

カーディナリティ:カラムに格納されているデータの種類がどのくらいあるのかをカーディナリティといい、データの種類が少ない場合はカーディナリティが低いといい、多い場合はカーディナリティが高いという。

以下に具体例を整理しています。

インデックス付き高カーディナリティフィールドでの検索

SELECT * FROM VehicleRegistration
WHERE VIN = ‘1N4AL11D75C109151’

インデックス付き高カーディナリティフィールドと別フィールドでの検索

SELECT * FROM VehicleRegistration
WHERE VIN = ‘1N4AL11D75C109151’ AND City = ‘Seattle’

IN句を活用した、インデックス付高カーディナリティフィールドでの検索

SELECT * FROM VehicleRegistration
WHERE VIN IN (‘1N4AL11D75C109151’, ‘KM8SRDHF6EU074761’)

BY句を使用したドキュメントIDでの検索

SELECT * FROM VehicleRegistration BY r_id
WHERE r_id = ‘3Qv67yjXEwB9SjmvkuG6Cp’

回避すべきクエリパターン

パフォーマンスが問題になってしまうクエリパターンとしては、低カーディナリティでの検索や、検索条件をつけないクエリが挙げられます。パフォーマンスだけでなく、テーブル全体スキャンを呼び出してしまい、トランザクションタイムアウトや同時実行による競合を呼び出してしまうため、避ける必要があります。

下記が推奨されないクエリのパターンです。

WHERE句のないクエリ

SELECT * FROM Vehicle

COUNT()関数

SELECT COUNT(*) FROM Vehicle

低カーディナリティでの検索

SELECT * FROM Vehicle WHERE Color = ‘Silver’

不等式はインデックス付きの検索対象にならない

SELECT * FROM Vehicle WHERE “Year” > 2019

LIKE句によるテキスト検索

SELECT * FROM Vehicle WHERE VIN LIKE ‘1N4AL%’

BETWEEN句による検索

SELECT SUM(PendingPenaltyTicketAmount) FROM VehicleRegistration
WHERE ValidToDate BETWEEN 2020-01-01T AND 2020-07-01T

ドキュメントIDと検索対象期間のない履歴クエリ

SELECT * FROM history(Vehicle)

まとめ

QLDBのデータ構造とPartiQLについて、また、クエリ実行に関して、最適化のためのクエリパターンや回避すべきクエリパターンについて整理しました。

参考

クエリパフォーマンスの最適化