はじめに
実務にて、Vue.js + Amplify + AppSync(graphQL) + DynamoDB 構成のアプリ画面機能にて検索速度の緩慢がみられたため、パフォーマンス向上策としてDynamoDBオペレーションの一つであるBatchGetItemの導入・改善対応を行いました。
下記はBatchGetItemの概要・導入方法、その他知ったことをまとめておきます。導入検討されてる方の助けになれば幸いです![]()
目次
🧷 DynamoDBとは?
振り返りですが、DynamoDBは、AWSが提供するフルマネージドのNoSQLデータベースサービスになります。
ざっくりいうと「管理の手間不要、どんなに規模が大きくなっても高速に応答してくれる、柔軟なデータベース」です。
✅ 主な特徴
- フルマネージド
サーバーの管理、OSのパッチ適用、ソフトウェアのアップデート、バックアップ、ハードウェアの拡張などを利用者が気にする必要がありません。
AWSが自動で行ってくれるため、開発者はアプリケーション開発に集中できます。
- NoSQLデータベース
MySQLやPostgreSQLのようなリレーショナルデータベース(RDB)とは異なり、柔軟なデータ構造を持つ「キーバリュー型」および「ドキュメント型」のデータベース。
「スキーマレス」とも呼ばれ、アイテム(データ行)ごとに異なる属性(データ列)を持つことができます。
- 高いパフォーマンス・スケーラビリティ
どれだけのデータ量やアクセス数であっても、一貫して数ミリ秒(1桁ミリ秒)という非常に高速な応答性能を維持できるように設計されています。
データ量やトラフィックの増減に合わせて、シームレスに性能をスケール(拡張・縮小)させることができます。
- 高い可用性と耐久性
データは自動的に一つのリージョン内の複数のアベイラビリティーゾーンに複製されます。
これにより、一部のサーバーや施設に障害が発生してもサービスは継続して利用でき、データが失われるリスクも非常に低くなっています。
✅ 用途
上記の特性から、以下のような高速なレスポンスと高い拡張性が求められる様々なアプリケーションで利用されています。
1. Webサイトのユーザープロファイル管理やセッション情報
2. モバイルアプリのバックエンド(ユーザーデータ、設定など)
3. オンラインゲームのランキングボードやプレイヤーデータ
4. IoTデバイスから送られてくる大量データ収集
5. Eコマースのショッピングカート情報
今回の実務アプリ画面での用途としては、4 が近いです。
🧷 DynamoDBオペレーションとは?
DynamoDBのテーブル・データに対して実行できるすべての操作のことをDynamoDBオペレーションと言います。
AWSが提供するAPI(コマンド群) と考えると分かりやすいです。
これらのオペレーションを使って、データの追加、読み取り、更新、削除(CRUD)や、テーブル自体の作成・管理などを行います。
✅ オペレーションの種類
大きく分けて以下のような種類があります。代表的なものは下記です。
1. データ操作オペレーション
テーブル内のデータを直接操作
-
PutItem:1つのアイテム(データ行)を新規作成または上書き保存 -
GetItem:プライマリキーを指定して1つのアイテムを取得 -
UpdateItem:既存のアイテムの属性を更新します(なければ新規作成も可能) -
DeleteItem:1つのアイテムを削除 -
BatchGetItem:複数のアイテムをまとめて取得 -
Query:特定のパーティションキーを持つアイテム群を効率的に検索・取得 -
Scan:テーブル内のすべてのアイテムを読み取り(コスト面で使用注意)
2. テーブル操作オペレーション
テーブル自体を管理
-
CreateTable:新しいテーブルを作成 -
DescribeTable:テーブルの状態(ステータス、スキーマなど)情報を取得 -
UpdateTable:テーブルの設定(プロビジョンドスループットなど)を変更 -
DeleteTable:テーブルを削除
3. トランザクションオペレーション
複数操作を「すべて成功」または「すべて失敗」のいずれかの結果扱いたい場合に使用
-
TransactWriteItems: 複数の書き込み(Put, Update, Delete)をまとめて実行 -
TransactGetItems: 複数の読み込み(Get)を実行
これらは代表的なもので、他にも多くのオペレーションが存在します。
それぞれのオペレーションが特定の目的のために設計されています。
🧷 BatchGetItemの概要
BatchGetItemは、DynamoDBのオペレーションの一つで、
1つ以上のテーブルから最大100個のアイテムを一度のリクエストで一括取得するための操作機能
になります。
今回の検索速度改善ですが、具体的な改修内容としては GetItem => BatchGetItem への変更対応を行いました。
GetItemだとデータ1件ずつの取得となり、DynamoDBの保存データ数分リクエストをかけることになるため、全データの処理終了にかなりの時間がかかります。
そこでBatchGetItemへの変更を行うことで、リクエスト回数を100倍減らす = 検索速度が速まる想定の下、導入を行いました。
🧷 コード改修
※ 改修内容については、各自の技術スタック・システム構成によるため、参考程度に閲覧ください。
✅ ディレクトリ構成
example-cdk/
AWS CDKによるインフラストラクチャ定義
- bin/:CDKアプリケーションのエントリーポイント
- example_admin-cdk.ts:CDKによるインフラストラクチャ定義
- lib/:スタック定義クラス
- amplify-stack.ts
- appsync-stack.ts
- cdkpipeline-stack.ts
- appsync/:GraphQLスキーマとVTLリゾルバーテンプレート
- schema.graphql
- config/:環境別設定ファイル
- test/:CDKスタックのユニットテス
- amplify-stack.test.ts
- appsync-stack.test.ts
nuxt/
Nuxt.jsフロントエンドアプリケーション
- pages/:アプリケーションのビューとルート
- components/:再利用可能なVueコンポーネント
- layouts/: レイアウトテンプレート
- middleware/:ルートミドルウェア
- plugins/:Vueプラグイン
- store/:Vuexストア
- src/graphql/:GraphQLクエリ定義
- src/utils/:ユーティリティ関数
- src/{env}/:環境別設定(dev/stg/prd)
- static/:静的ファイル
上記が大まかなプロジェクト構成になります。
以降は、本題に関わるソースコードのみ抜粋紹介とさせていただきます。
✅ CDKインフラストラクチャ定義
//appsync用role
const appsyncRole = new iam.Role(this, 'appsync-role', {
roleName: 'appsyncRole',
assumedBy: new iam.ServicePrincipal('appsync.amazonaws.com'),
});
appsyncRole.attachInlinePolicy(
new iam.Policy(this, 'appsync-policy', {
statements: [
new iam.PolicyStatement({
actions: ['dynamodb:GetItem', 'dynamodb:BatchGetItem', 'dynamodb:Query', 'dynamodb:Scan'], // BatchGetItemをPolicyStatement追加
resources: [dataTableArn, dataDetailTableArn],
}),
],
})
);
api.grantQuery(appsyncRole, 'appsync:GraphQL');
✅ GraphQLスキーマ
input DataKey {
pfPrimaryKey: String!
pfSortKey: String!
}
type DataTable @aws_api_key @aws_cognito_user_pools {
id: String
userId: String
createdAt: String
updatedAt: String
}
type Query @aws_api_key @aws_cognito_user_pools {
getDataTable(keys: [DataKey!]!): [DataTable]
}
✅ BatchGetItem用リゾルバー定義
変更箇所
- CfnResolver第二引数(
'getDataTable'):ユニークな名称指定 - fieldName:ユニークな名称指定
- requestMappingTemplate
- version:"2018-05-29"を指定
- operation:
"BatchGetItem"を指定 - tables:DeveloperGuide参考に
keys配列でテーブルアイテムを囲う形とする
const uniqueDataTable = new appsync.CfnResolver(
this,
'uniqueDataTable',
{
apiId: api.apiId,
typeName: 'Query',
fieldName: 'uniqueDataTable',
dataSourceName: DataTableDataSource.name,
requestMappingTemplate: `{
"version": "2018-05-29",
"operation": "BatchGetItem",
"tables": {
"${DataTableName}": {
"keys": [
#foreach($key in $ctx.args.keys)
{
"pfPrimaryKey": $util.dynamodb.toDynamoDBJson($key.pfPrimaryKey),
"pfSortKey": $util.dynamodb.toDynamoDBJson($key.pfSortKey)
}#if($foreach.hasNext),#end
#end
]
}
}
}`,
responseMappingTemplate: `$util.toJson($context.result.data.DataTable)`,
}
);
uniqueDataTable.addDependsOn(DataTableDataSource);
⚠️ requestMappingTemplate.versionに注意
変更前のGetItem用のversion("2017-02-28")のままの場合、Unsupported operation BatchGetItemエラー となるので注意
CfnResolver・requestMappingTemplate
どちらもAWS AppSyncというサービスでGraphQL APIをAWS CloudFormationを使って定義する際に使われる重要な要素になります。
- CfnResolver
- GraphQLの特定のフィールドと、そのデータを実際に取得・保存するデータソース(DBやLambdaなど)を繋ぐための設定
- GraphQLのフィールドとデータソースの「仲介役」 のような役割を果たします。
- requestMappingTemplate
- CfnResolverリソースのプロパティ(設定項目)の一つで、GraphQLリクエストをデータソースが理解できる命令形式に変換するためのテンプレート
- テンプレートは、VTL (Apache Velocity Template Language) 言語で記述
CfnResolverが 「処理の箱」 だとすれば、requestMappingTemplateは その箱の中で「リクエストをどう翻訳・加工するか」を定義する設計図の一部のようなイメージ
ちなみに、requestMappingTemplate.versionのことをVTLテンプレートバージョンと呼ばれます。
✅ フロントエンドアプリケーション
修正前
// GetItemでデータ1件ずつ取得
for (const items of deviceList.data.BiginDataTableByGSI2.items) {
const dataDetail = await API.graphql({
query: queries.getDataTable,
variables: {
pfPrimaryKey: items.pfPrimaryKey,
pfSortKey: items.pfPrimaryKey,
},
})
}
修正後
// データ一括取得時に使用するPK・SKのリスト作成
const keys = dataList.data.BiginDataTableByGSI2.items.map(
(item) => ({
pfPrimaryKey: item.pfPrimaryKey,
pfSortKey: item.pfPrimaryKey,
})
)
// BatchGetItemでデータ一括取得
if (keys.length > 0) {
const dataDetail = await API.graphql({
query: queries.getDataTable,
variables: {
keys,
},
})
}
💡BatchGetItemにすることで1回のリクエストでMAX100件のデータ処理が可能に!
💡for文が不要!
🧷 まとめ
- 高速なNoSQLデータベースDynamoDBの検索速度を改善するため、データ取得方法を変更します。
- 1件ずつ取得する
GetItemから、複数件を一括取得するBatchGetItemへ切り替えることで、リクエスト回数を削減し高速化を図ります。 - 実装には、IAMポリシーへの権限追加や、AppSyncリゾルバーの変更が必要です。特に、リクエストテンプレートのバージョンを
"2018-05-29"に指定する点は要注意!