【新サービス】改竄不能な台帳データベースAmazon QLDBが利用可能になりました

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

大栗です。

re:Invent 2018で発表されていた台帳データベースのAmazon Quantum Ledger Database(QLDB)が利用可能となったのでご紹介します。

Amazon Quantum Ledger Database

Amazon Quantum Ledger Database(QLDB)はデータの更新/削除の完全で不変な変更を保持するデータベースです。PartiQLによるSQLクエリをサポートしており、履歴が正当であることを検証できます。銀行などの金融機関やeコマース、輸送物流、人事給与、政府系アプリケーションなどデータの整合性と履歴を維持する必要があるユースケースに適合します。

Hyperledger Fabric、Ethereumなどのブロックチェーンフレームワークにより自前に台帳データベースを運用することも可能ですが、開発や運用が必要となります。Amazon QLDBではマネージドに変更履歴を維持する台帳データベースを簡単に利用できます。

詳しくは、以下のエントリを御覧ください。

[新サービス] フルマネージドの台帳データベースである、Amazon Quantum Ledger Database (QLDB) が発表されました! #reinvent

[レポート]DAT378:台帳データベースはどう使う?Amazon QLDB入門 #reinvent

またSQL互換のPartiQLについてはこちらのエントリを御覧ください。

AWSのデータストアやフォーマットに依存しない問い合わせが可能に。オープンソースとして公開されたSQL互換クエリ言語「PartiQL」を触ってみた

対象リージョン

現在利用可能なリージョンは以下となります。東京が入っていますね。

  • 米国東部 (バージニア北部)
  • 米国東部 (オハイオ)
  • 米国西部 (オレゴン)
  • アジアパシフィック (東京)
  • EU (アイルランド)

料金

東京リージョンの料金は以下となります。

内容 料金
書き込み I/O 0.799 USD/100 万リクエスト
読み込み I/O 0.155 USD/100 万リクエスト
ジャーナルストレージ料金 月額料金 0.034 USD/GB
インデックス化ストレージ料金 月額料金 0.285 USD/GB

やってみる

東京リージョンを前提として以下の操作を実施します。

台帳の作成

QLDBのコンソールを開き台帳の作成をクリックします。

台帳名に一意な名称を入力します。ここではBlogLedgerとします。そして台帳の作成をクリックします。

しばらくすると台帳が作成されます。作成時刻はUTCで表示されるようです。

サンプルデータの作成

次にサンプルデータを作成します。ここではドキュメントのチュートリアルにある内容を使用します。これは車両登録のデータです。

まずクエリエディタを開き、作成した台帳を選択します。

以下の4つのテーブルを作成します。

  • VehicleRegistration
  • Vehicle
  • Person
  • DriversLicense

以下のように1テーブルずつ作成して下さい。複文はサポートされていないようです。

CREATE TABLE VehicleRegistration
CREATE TABLE Vehicle
CREATE TABLE Person
CREATE TABLE DriversLicense

実行すると以下のようにtableidがされます。

次にインデックスを作成します。

CREATE INDEX ON VehicleRegistration (VIN)
CREATE INDEX ON VehicleRegistration (LicensePlateNumber)
CREATE INDEX ON Vehicle (VIN)
CREATE INDEX ON Vehicle (VIN)
CREATE INDEX ON DriversLicense (LicenseNumber)
CREATE INDEX ON DriversLicense (PersonId)

以下のようにインデックスが6個できました。

次にテーブルへデータをロードします。

INSERT INTO Person
<< {
    'FirstName' : 'Raul',
    'LastName' : 'Lewis',
    'DOB' : `1963-08-19T`,
    'GovId' : 'LEWISR261LL',
    'GovIdType' : 'Driver License',
    'Address' : '1719 University Street, Seattle, WA, 98109'
},
{
    'FirstName' : 'Brent',
    'LastName' : 'Logan',
    'DOB' : `1967-07-03T`,
    'GovId' : 'LOGANB486CG',
    'GovIdType' : 'Driver License',
    'Address' : '43 Stockert Hollow Road, Everett, WA, 98203'
},
{
    'FirstName' : 'Alexis',
    'LastName' : 'Pena',
    'DOB' : `1974-02-10T`,
    'GovId' : '744 849 301',
    'GovIdType' : 'SSN',
    'Address' : '4058 Melrose Street, Spokane Valley, WA, 99206'
},
{
    'FirstName' : 'Melvin',
    'LastName' : 'Parker',
    'DOB' : `1976-05-22T`,
    'GovId' : 'P626-168-229-765',
    'GovIdType' : 'Passport',
    'Address' : '4362 Ryder Avenue, Seattle, WA, 98101'
},
{
    'FirstName' : 'Salvatore',
    'LastName' : 'Spencer',
    'DOB' : `1997-11-15T`,
    'GovId' : 'S152-780-97-415-0',
    'GovIdType' : 'Passport',
    'Address' : '4450 Honeysuckle Lane, Seattle, WA, 98101'
} >>
INSERT INTO DriversLicense
<< {
    'LicenseNumber' : 'LEWISR261LL',
    'LicenseType' : 'Learner',
    'ValidFromDate' : `2016-12-20T`,
    'ValidToDate' : `2020-11-15T`,
    'PersonId' : ''
},
{
    'LicenseNumber' : 'LOGANB486CG',
    'LicenseType' : 'Probationary',
    'ValidFromDate' : `2016-04-06T`,
    'ValidToDate' : `2020-11-15T`,
    'PersonId' : ''
},
{
    'LicenseNumber' : '744 849 301',
    'LicenseType' : 'Full',
    'ValidFromDate' : `2017-12-06T`,
    'ValidToDate' : `2022-10-15T`,
    'PersonId' : ''
},
{
    'LicenseNumber' : 'P626-168-229-765',
    'LicenseType' : 'Learner',
    'ValidFromDate' : `2017-08-16T`,
    'ValidToDate' : `2021-11-15T`,
    'PersonId' : ''
},
{
    'LicenseNumber' : 'S152-780-97-415-0',
    'LicenseType' : 'Probationary',
    'ValidFromDate' : `2015-08-15T`,
    'ValidToDate' : `2021-08-21T`,
    'PersonId' : ''
} >>
INSERT INTO VehicleRegistration
<< {
    'VIN' : '1N4AL11D75C109151',
    'LicensePlateNumber' : 'LEWISR261LL',
    'State' : 'WA',
    'City' : 'Seattle',
    'PendingPenaltyTicketAmount' : 90.25,
    'ValidFromDate' : `2017-08-21T`,
    'ValidToDate' : `2020-05-11T`,
    'Owners' : {
        'PrimaryOwner' : { 'PersonId': '' },
        'SecondaryOwners' : []
    }
},
{
    'VIN' : 'KM8SRDHF6EU074761',
    'LicensePlateNumber' : 'CA762X',
    'State' : 'WA',
    'City' : 'Kent',
    'PendingPenaltyTicketAmount' : 130.75,
    'ValidFromDate' : `2017-09-14T`,
    'ValidToDate' : `2020-06-25T`,
    'Owners' : {
        'PrimaryOwner' : { 'PersonId': '' },
        'SecondaryOwners' : []
    }
},
{
    'VIN' : '3HGGK5G53FM761765',
    'LicensePlateNumber' : 'CD820Z',
    'State' : 'WA',
    'City' : 'Everett',
    'PendingPenaltyTicketAmount' : 442.30,
    'ValidFromDate' : `2011-03-17T`,
    'ValidToDate' : `2021-03-24T`,
    'Owners' : {
        'PrimaryOwner' : { 'PersonId': '' },
        'SecondaryOwners' : []
    }
},
{
    'VIN' : '1HVBBAANXWH544237',
    'LicensePlateNumber' : 'LS477D',
    'State' : 'WA',
    'City' : 'Tacoma',
    'PendingPenaltyTicketAmount' : 42.20,
    'ValidFromDate' : `2011-10-26T`,
    'ValidToDate' : `2023-09-25T`,
    'Owners' : {
        'PrimaryOwner' : { 'PersonId': '' },
        'SecondaryOwners' : []
    }
},
{
    'VIN' : '1C4RJFAG0FC625797',
    'LicensePlateNumber' : 'TH393F',
    'State' : 'WA',
    'City' : 'Olympia',
    'PendingPenaltyTicketAmount' : 30.45,
    'ValidFromDate' : `2013-09-02T`,
    'ValidToDate' : `2024-03-19T`,
    'Owners' : {
        'PrimaryOwner' : { 'PersonId': '' },
        'SecondaryOwners' : []
    }
} >>
INSERT INTO Vehicle
<< {
    'VIN' : '1N4AL11D75C109151',
    'Type' : 'Sedan',
    'Year' : 2011,
    'Make' : 'Audi',
    'Model' : 'A5',
    'Color' : 'Silver'
},
{
    'VIN' : 'KM8SRDHF6EU074761',
    'Type' : 'Sedan',
    'Year' : 2015,
    'Make' : 'Tesla',
    'Model' : 'Model S',
    'Color' : 'Blue'
},
{
    'VIN' : '3HGGK5G53FM761765',
    'Type' : 'Motorcycle',
    'Year' : 2011,
    'Make' : 'Ducati',
    'Model' : 'Monster 1200',
    'Color' : 'Yellow'
},
{
    'VIN' : '1HVBBAANXWH544237',
    'Type' : 'Semi',
    'Year' : 2009,
    'Make' : 'Ford',
    'Model' : 'F 150',
    'Color' : 'Black'
},
{
    'VIN' : '1C4RJFAG0FC625797',
    'Type' : 'Sedan',
    'Year' : 2019,
    'Make' : 'Mercedes',
    'Model' : 'CLK 350',
    'Color' : 'White'
} >>

以下のようにデータごとにdocumentidが出力されます。

データの問い合わせ

ロードしたデータに対して問い合わせをしてみます。基本的にSQL形式でアクセスします。

SELECT * FROM Vehicle AS v
WHERE v.VIN = '1N4AL11D75C109151'
VIN Type Year Make Model Color
"1N4AL11D75C109151" "Sedan" 2011 "Audi" "A5" "Silver"
SELECT v.VIN, r.LicensePlateNumber, r.State, r.City, r.Owners
FROM Vehicle AS v, VehicleRegistration AS r
WHERE v.VIN = '1N4AL11D75C109151'
AND v.VIN = r.VIN
VIN LicensePlateNumber State City Owners
"1N4AL11D75C109151" "LEWISR261LL" "WA" "Seattle" {PrimaryOwner:{PersonId:""},SecondaryOwners:[]}
SELECT * FROM Person AS p, DriversLicense as l
WHERE p.GovId = l.LicenseNumber
FirstName LastName DOB GovId GovIdType Address LicenseNumber LicenseType ValidFromDate ValidToDate PersonId
"Alexis" "Pena" 1974-02-10T "744 849 301" "SSN" "4058 Melrose Street, Spokane Valley, WA, 99206" "744 849 301" "Full" 2017-12-06T 2022-10-15T ""
"Brent" "Logan" 1967-07-03T "LOGANB486CG" "Driver License" "43 Stockert Hollow Road, Everett, WA, 98203" "LOGANB486CG" "Probationary" 2016-04-06T 2020-11-15T ""
"Salvatore" "Spencer" 1997-11-15T "S152-780-97-415-0" "Passport" "4450 Honeysuckle Lane, Seattle, WA, 98101" "S152-780-97-415-0" "Probationary" 2015-08-15T 2021-08-21T ""
"Melvin" "Parker" 1976-05-22T "P626-168-229-765" "Passport" "4362 Ryder Avenue, Seattle, WA, 98101" "P626-168-229-765" "Learner" 2017-08-16T 2021-11-15T ""
"Raul" "Lewis" 1963-08-19T "LEWISR261LL" "Driver License" "1719 University Street, Seattle, WA, 98109"

データの変更

_ql_committed_プレフィックスは対象ドキュメントのコミット済みビューを表します。ここではLewisの初回登録したときのdocumentidを問い合わせています。

SELECT metadata.id FROM _ql_committed_Person AS p
WHERE p.data.FirstName = 'Raul' and p.data.LastName = 'Lewis'
id
"LI6ChXXLwloBp05WWwtigT"

次にdocumentidを利用して、VehicleRegistrationテーブルへ所有者情報をセットします。

UPDATE VehicleRegistration AS r
SET r.Owners.PrimaryOwner.PersonId = 'LI6ChXXLwloBp05WWwtigT'
WHERE r.VIN = '1N4AL11D75C109151'

更新したデータを確認すると、documentidが登録されています。

SELECT r.Owners FROM VehicleRegistration AS r
WHERE r.VIN = '1N4AL11D75C109151'
Owners
{PrimaryOwner:{PersonId:"LI6ChXXLwloBp05WWwtigT"},SecondaryOwners:[]}

次に車をBrentからEverettへ譲渡します。

まずはBrentのdocumentidを取得します。

SELECT metadata.id FROM _ql_committed_Person AS p
WHERE p.data.FirstName = 'Brent' and p.data.LastName = 'Logan'
id
"AjNBWHzeKNIHQCttIEPEbj"

このdocumentidをPrimaryOwnerとしてVehicleRegistrationテーブルを更新します。

UPDATE VehicleRegistration AS r
SET r.Owners.PrimaryOwner.PersonId = 'AjNBWHzeKNIHQCttIEPEbj', r.City = 'Everett'
WHERE r.VIN = '1N4AL11D75C109151'

変更結果を確認します。

SELECT r.Owners.PrimaryOwner, r.City
FROM VehicleRegistration AS r
WHERE r.VIN = '1N4AL11D75C109151'
PrimaryOwner City
{PersonId:"AjNBWHzeKNIHQCttIEPEbj"} "Everett"

次にAlexisを副所有者として追加します。

まずはAlexisのdocumentidを検索します。

SELECT metadata.id FROM _ql_committed_Person AS p
WHERE p.data.FirstName = 'Alexis' and p.data.LastName = 'Pena'
id
"7YQHDHLkYw57osOjH3PGRG"

取得したidをSecondaryOwnersに挿入します。ここではFROM-INSERT文を使用します。

FROM VehicleRegistration AS r 
WHERE r.VIN = '1N4AL11D75C109151'
INSERT INTO r.Owners.SecondaryOwners
    VALUE { 'PersonId' : '7YQHDHLkYw57osOjH3PGRG' }

挿入したデータを確認します。

SELECT r.Owners.SecondaryOwners FROM VehicleRegistration AS r
WHERE r.VIN = '1N4AL11D75C109151'
SecondaryOwners
[{PersonId:"7YQHDHLkYw57osOjH3PGRG"}]

変更履歴を確認する

変更したデータの履歴を確認します。

まず対象車両のdocumentidを確認します。

SELECT r_id FROM VehicleRegistration AS r BY r_id
WHERE r.VIN = '1N4AL11D75C109151'
r_id
"LY4HGawYaDTCQJHke8ONWI"

取得したidを元に履歴を確認します。history関数で履歴情報を取得します。history関数では履歴の開始終了を指定することもできます。

SELECT h.data.VIN, h.data.City, h.data.Owners
FROM history(VehicleRegistration) AS h
WHERE h.metadata.id = 'LY4HGawYaDTCQJHke8ONWI'
VIN City Owners
"1N4AL11D75C109151" "Seattle" {PrimaryOwner:{PersonId:""},SecondaryOwners:[]}
"1N4AL11D75C109151" "Seattle" {PrimaryOwner:{PersonId:"LI6ChXXLwloBp05WWwtigT"},SecondaryOwners:[]}
"1N4AL11D75C109151" "Everett" {PrimaryOwner:{PersonId:"AjNBWHzeKNIHQCttIEPEbj"},SecondaryOwners:[]}
"1N4AL11D75C109151" "Everett" {PrimaryOwner:{PersonId:"AjNBWHzeKNIHQCttIEPEbj"},SecondaryOwners:[{PersonId:"7YQHDHLkYw57osOjH3PGRG"}]}

また各リビジョンのドキュメントのメタデータも検査できます。

SELECT VALUE h.metadata
FROM history(VehicleRegistration) AS h
WHERE h.metadata.id = 'LY4HGawYaDTCQJHke8ONWI'
id version txTime txId
"LY4HGawYaDTCQJHke8ONWI" 0 2019-09-11T01:16:31.751Z "6mW3W78sqbsHHyryF3CGYr"
"LY4HGawYaDTCQJHke8ONWI" 1 2019-09-11T02:40:07.348Z "EqsMZAXf6bCHOaoSP1zu9f"
"LY4HGawYaDTCQJHke8ONWI" 2 2019-09-11T02:48:09.947Z "DThyDXjR8VJ63mv3Fa9obl"
"LY4HGawYaDTCQJHke8ONWI" 3 2019-09-11T02:56:50.003Z "4I9LNv13L8c3C7MEBkq6Om"

|id|システムが割り当てた一意に識別できるID| |version|| |txTime|作成された時刻| |txId|トランザクションを一意に識別できるID|

ドキュメントを検証する

Amazon QLDBはドキュメントの整合性の検証ができます。

まずクエリエディタから以下のクエリでVehicleRegistrationテーブルのビューから取得します。

SELECT r.metadata.id, r.blockAddress
FROM _ql_committed_VehicleRegistration AS r 
WHERE r.data.VIN = '1N4AL11D75C109151'
id blockAddress
"LY4HGawYaDTCQJHke8ONWI" {strandId:"8PdWQnKPj08Cf5AVnvLMXS",sequenceNo:104}

次に台帳自体の自体のダイジェストを取得します。

対象の台帳を選択して、ダイジェストを取得をクリックします。

保存をクリックして取得したダイジェストを保存します。するとダイジェストがAmazon Ionの形式でダウンロードされます。

そしてQLDBのコンソールで検証を表示します。

取得した検証対象ドキュメントのブロックアドレス、ドキュメント IDを入力して、ダウンロードした台帳のダイジェストをアップロードします。そして検証をクリックします。

すると検証結果が表示されます。

証明の内容は以下のようになります。

ブロックは以下のようになります。

これで更新内容の検証が行えました。

さいごに

SQLでアクセスできますが、通常のユースケースで使用するためのデータベースではありません。データの変更を厳密に管理する必要がある場合はQLDBを活用する場面が出てくるのではないかと思います。