JDBCレイヤでDBのシャーディングを行うsharding-jdbcを試してみた

DBのデータ量が増えてきた場合の対策の1つとしてユーザIDなどをキーにデータベースを分割するシャーディングと呼ばれる手法があります。これをJDBCのレイヤで実現してしまうsharding-jdbcというライブラリを見つけました。

github.com

sharding-jdbcは中国のdangdang(当当)というEC大手企業が開発したOSSで、SQLをパースし、SQLに含まれるシャードキーを抽出して接続先のデータベースや、参照するテーブルを切り替えてくれるというものです。

使ってみる

まずはpom.xmlに以下の依存関係を追加します。

<dependency>
    <groupId>com.dangdang</groupId>
    <artifactId>sharding-jdbc-core</artifactId>
    <version>1.4.1</version>
</dependency>

今回は簡単な例ということで、COMPANY_IDというカラムをシャードキーに接続するデータベースを切り替えてみます。以下のような感じでデータソースを準備します。

// 接続先のデータソースを作成
Map<String, DataSource> dataSources = new HashMap<>();
dataSources.put("ds_0", createDataSource(...));
dataSources.put("ds_1", createDataSource(...));
DataSourceRule dataSourceRule = new DataSourceRule(dataSources);

// テーブルごとのルールを作成
TableRule companyTableRule = TableRule.builder("COMPANY")
  .dataSourceRule(dataSourceRule)
  .build();

// シャーディングのルールを作成
ShardingRule shardingRule = ShardingRule.builder()
  .dataSourceRule(dataSourceRule)
  .tableRules(Arrays.asList(companyTableRule, deptTableRule))
  .databaseShardingStrategy(new DatabaseShardingStrategy("COMPANY_ID", new CompanyIdShardingAlgorithm()))
  .build();

DataSource dataSource = ShardingDataSourceFactory.createDataSource(shardingRule);

CompanyIdShardingAlgorithmが接続先データベースを決定するクラスになります。シャードキーに使用するカラムは=BETWEENINでの検索しか行うことができません。ShardingAlgorithmではこれらの検索方法に対応したメソッドを実装する必要があるのですが、今回は=での検索しか行わないのでdoEqualSharding()メソッドのみ実装しています。

public class CompanyIdShardingAlgorithm implements SingleKeyDatabaseShardingAlgorithm<String> {

  @Override
  public String doEqualSharding(Collection<String> availableTargetNames, ShardingValue<String> shardingValue) {
    switch(shardingValue.getValue()){
        case "bizreach":
          return "ds_0";
        default:
          return "ds_1";
    }
  }

  @Override
  public Collection<String> doInSharding(Collection<String> availableTargetNames, ShardingValue<String> shardingValue) {
    throw new UnsupportedOperationException();
  }

  @Override
  public Collection<String> doBetweenSharding(Collection<String> availableTargetNames, ShardingValue<String> shardingValue) {
    throw new UnsupportedOperationException();
  }
}

あとは作成したデータソースからgetConnection()メソッドを取得してSQLを実行すればシャードキーに応じたDBに対してSQLが発行されます。

その他の機能

sharding-jdbcにはこの他にも以下のような機能があります。

  • シャードキーで、接続するデータベースではなく参照するテーブルを切り替える機能(SQLがリライトされて実行されます)
  • 一意なIDを発行する機能(シャードをまたぐ自動採番のカラムに値を入れるのに使用します)
  • クエリが複数のシャードにまたがる場合にORDER BYGROUP BYを処理する機能(マージ処理と呼ぶようです)

一方で前述の通りシャードキーに対する検索条件に制限がある、異なるシャードをUNIONできないなどの制約もあります。正常にルーティングが行われないSQLの場合でも特にエラーにはならず、SQL中で最初に検出されたシャードキーを使ってルーティングされているように見えましたが、できればエラーになってくれると嬉しいですね。

まとめ

sharding-jdbcJDBCのレイヤでシャーディングを解決してくれるため、既存の様々なORMやデータベースアクセスライブラリをそのまま利用できるという大きなメリットがあります。利用可能なSQLに制限はあるものの、そもそもシャードキーに対してリッチな検索を行う必要はないでしょうし、複雑なレポーティングが必要といったケースでなければさほど困ることもなさそうです。

現時点でシャーディングが不要だとしても、将来的にデータの増加に伴ってDBのシャーディングを検討する可能性があるようであれば、最初からこれを入れておくという手も考えられるのではないでしょうか。