5.9. データベースシャーディング

5.9.1. Overview

本ガイドラインでは、AWSを使用したデータベースシャーディングについて説明する。

データ永続層のスケール性の確保データベースシャーディング で説明したシャーディング方式を実現するにあたり、AWS上でシャーディングを実現する場合はシャードにはRDSを、シャードキー管理のための記憶装置にはKVS(Key-Value Store)であるDynamoDBを使用する。また、DynamoDBに格納するシャードキー情報は読み込み頻度が高く、値の更新の機会が少ないため、キャッシュ方式によりアクセスの負荷を低減する。DynamoDBへのアクセスにはAWS Java SDK DynamoDBを使用する。シャードキー情報のキャッシュ化には キャッシュの抽象化(Cache Abstraction) を使用する。

本ガイドラインでは、以下に示すイメージの赤枠破線部分について説明する。

../_images/DatabaseShardingOverview.png
項番 説明
(1)
Controllerが@ShardWithAccount@Transactional付のServiceメソッドを呼び出す。
(2)
Sharding AOPがDynamoDB Repositoryを呼び出しシャードキーを特定する。
(3)
Sharding AOPは、(2)で特定したシャードキーをRouting Data Sourceへ伝播する。
(4)
Transaction AOPは、Transaction Managerを呼び出す。
(5)
Transaction Managerは、Routing Data SourceからConnectionを取得する。
(6)
Transaction Managerは、(5)で取得したConnectionでトランザクションを開始しConnection HolderへConnectionを格納する。
(7)
Serviceは、Shard RepositoryのDBアクセスメソッドを呼び出す。
(8)
Shard Repositoryは、Mybatis Springを経由してDBへクエリを発行する。
(9)
Mybatis Springは、(6)で格納したConnectionをConnection Holderから取得しDBへアクセスする。

5.9.1.1. DynamoDB

DynamoDBについては、 公式サイト を参照されたい。

DynamoDBのガイドについては、 Amazon DynamoDB ドキュメント を参照されたい。

Warning

DynamoDBにトランザクション機能はないため、RDB等との整合性をトランザクション機能により担保することができない点に注意されたい。 また、デフォルトでは結果整合性モデルである点に注意されたい。詳細は 読み込み整合性 を参照されたい。

5.9.2. How to use

AWSを使用する場合、データベースシャーディング で説明したシャードキーの取得の内容は、本ガイドラインで説明する以下の内容となる。

DynamoDBへアクセスするシャードキーリポジトリの設定シャードキーリポジトリの実装について説明する。


5.9.2.1. シャードキーリポジトリの設定

以下に、pom.xmlで依存ライブラリの設定例を示す。

  • xxx-domain/pom.xml
・・・
<!-- (1) -->
<dependency>
  <groupId>com.amazonaws</groupId>
  <artifactId>aws-java-sdk-dynamodb</artifactId>
</dependency>
・・・
項番 説明
(1)
aws-java-sdk-dynamodbを設定する。

以下に、DynamoDBのリージョンとシャードキーリポジトリインタフェースの設定例を示す。

  • xxx-env/src/main/resources/application-local.ymlにDynamoDBのリージョンを設定
cloud:
  aws:
    ・・・
    # (1)
    dynamodb:
      region: ap-northeast-1
項番 説明
(1)
DynamoDBのリージョンを設定する。

  • xxx-domain/src/main/resources/META-INF/spring/xxx-domain.xmlにシャードキーリポジトリインタフェースの設定
<!-- (1) -->
 <bean id="amazonDynamoDB"
   class="com.example.xxx.domain.common.dynamodb.DynamoDBClientFactory" factory-method="create">
   <!-- (2) -->
   <constructor-arg index ="0" value="${cloud.aws.dynamodb.region}" />
 </bean>
<!-- (3) -->
<bean id="AccountShardKeyRepository"
  class="com.example.xxx.domain.common.shard.repository.AccountShardKeyRepositoryImpl" />
項番 説明
(1)
DynamoDBへアクセスするためのAmazonDynamoDBを定義する。
DynamoDBClientFactoryを使用してインスタンスを生成する。
(2)
DynamoDBリージョンをコンストラクタ引数で設定する。
(3)
シャードキーリポジトリの実クラスをスキャン対象に設定する。

  • DynamoDBClientFactory.java
public class DynamoDBClientFactory {
     public static AmazonDynamoDB create(String region) {
        // (1)
        return AmazonDynamoDBClientBuilder.standard().withRegion(region).build();
    }
}
項番 説明
(1)
AmazonDynamoDBClientBuilderを使用してインスタンスを生成する。

5.9.2.2. シャードキーリポジトリの実装

以下に、DynamoDBのテーブルShardAccountに格納されたデータのイメージを示す。

user_id data_source_key
0000000001
xxx1
0000000002
xxx2
0000000003
xxx3
・・・
・・・

Note

DynamoDBは、AWSアカウントとリージョンの組み合わせの単位でテーブル名がユニークとなっている必要がある。そのため、たとえば開発環境とテスト環境で同一のAWSアカウントとリージョンを使用して、データを排他的に管理したい場合は同名のテーブルを使用することができない。

そのため、同一アプリケーションを複数の排他的な環境で実行したい場合には、AWSアカウントもしくはリージョンを別にすることを推奨する。


DynamoDBへアクセスする為には、テーブルデータに対応したエンティティクラスとシャードキーリポジトリクラスを作成する。 以下に、DynamoDBのエンティティクラスShardingAccountとシャードキーリポジトリクラスAccountShardKeyRepositoryの実装例を示す。

  • エンティティクラスShardingAccount
package com.example.xxx.domain.common.shard.model;

import java.io.Serializable;

public class ShardingAccount implements Serializable {

  private static final long serialVersionUID = 1L;
  // (1)
  private String id;
  // (2)
  private String dataSourceKey;
  // setter and getter
}
項番 説明
(1)
ユーザID(ハッシュキー)の項目名を定義する。
(2)
データソースキーの項目名を定義する。

  • リポジトリクラスAccountShardKeyRepository
package com.example.xxx.domain.common.shard.repository;

import org.springframework.data.repository.CrudRepository;

import com.example.xxx.domain.common.shard.model.ShardingAccount;

public interface AccountShardKeyRepository {

  Optional<ShardingAccount> findById(String id);

  void save(ShardingAccount entity);
}

5.9.2.3. シャードキーリポジトリの使用

シャードキーリポジトリを利用するために実クラスAccountShardKeyRepositoryImplの実装例を示す。

詳細については AWS SDK for Java を使用した DynamoDB の例 を参考されたい。

  • リポジトリ実クラスAccountShardKeyRepositoryImpl
package com.example.xxx.domain.common.shard.repository;

import com.amazonaws.services.dynamodbv2.AmazonDynamoDB;
import com.amazonaws.services.dynamodbv2.model.AttributeValue;
import com.amazonaws.services.dynamodbv2.model.GetItemRequest;
import com.amazonaws.services.dynamodbv2.model.GetItemResult;
import com.amazonaws.services.dynamodbv2.model.PutItemRequest;

import com.example.xxx.domain.common.shard.model.ShardingAccount;

public class AccountSharedKeyRepositoryImpl implements AccountShardKeyRepository {

    private final AmazonDynamoDB amazonDynamoDB;
    // (1)
    private static final String DYNAMODB_TABLENAME = "ShardAccount";
    // (2)
    private static final String DYNAMODB_PRIMARYKEY = "user_id";
    // (3)
    private static final String DYNAMODB_ATTRIBUTE = "data_source_key";

    public AccountSharedKeyRepositoryImpl(AmazonDynamoDB amazonDynamoDB) {
        this.amazonDynamoDB = amazonDynamoDB;
    }

    @Override
    public Optional<ShardingAccount> findById(String id) {
        final ShardingAccount shardingAccount = new ShardingAccount();
        shardingAccount.setId(id);

        final Map<String, AttributeValue> key = new HashMap<>();
        key.put(DYNAMODB_PRIMARYKEY, new AttributeValue().withS(id));
        final GetItemRequest request = new GetItemRequest()
                .withTableName(DYNAMODB_TABLENAME)
                .withKey(key);

        final GetItemResult item = amazonDynamoDB.getItem(request);
        shardingAccount.setDataSourceKey(item.getItem().get(DYNAMODB_ATTRIBUTE).getS());

        return Optional.of(shardingAccount);
    }

    @Override
    public void save(ShardingAccount shardingAccount) {

        final PutItemRequest request = new PutItemRequest()
                .withTableName(DYNAMODB_TABLENAME)
                .addItemEntry(DYNAMODB_PRIMARYKEY, new AttributeValue(shardingAccount.getId()))
                .addItemEntry(DYNAMODB_ATTRIBUTE, new AttributeValue(shardingAccount.getDataSourceKey()));

        amazonDynamoDB.putItem(request);
    }
}
項番 説明
(1)
DynamoDBのテーブルを定義する。
(2)
ユーザID(ハッシュキー)の項目名を定義する。
(3)
データソースキーの項目名を定義する。