Scalaでgree/auroraを使って複数DB
この記事はScala Advent Calendar 2014の19日目です(遅れてしまってスミマセン…)。18日は ysksuzuki さんの Apache SparkのScala shellを試す - Qiita でした。
はじめに
先日 Rails複数DB Casual Talks - connpass が開かれるなど、複数DBの機運が高まっております。そこで今回はauroraというライブラリを使ってMySQLをシャーディングする例について書こうと思います。
auroraについて
GREEが公開しているJava/Scala向けのシャーディングライブラリです。Scala Matsuriで紹介されていました。 [ScalaMatsuri] グリー初のscalaプロダクト!チャットサービス公開までの苦労と工夫
設定ファイルに接続先のDBサーバの情報を書いておき、アプリケーション側からヒントを渡すことでそれに応じたデータソース/テーブル名を解決することが出来ます。
テーブル分割してみる
https://github.com/gree/aurora/blob/develop/README.md が充実しており、データソースをシャーディングする場合についてはそちらを参照してもらうのが早いかと思います。そのためここではREADMEで言及されていなかったテーブルをシャーディングする場合について見て行きたいと思います。
前提
以下のようにテーブルが準備されていることを想定します。ユーザIDの10で割った余剰に基づきテーブルを分割し、それぞれ異なるDB上に定義されています。
DBは2つ用意しており、DB1が 192.168.33.10:3306
、DB2が 192.168.33.10:3307
で稼働しています。
- DB構成
DBサーバ | DB名 | テーブル名 |
---|---|---|
DB1 | user | user_0 |
DB1 | user | user_1 |
DB1 | user | user_2 |
DB1 | user | user_3 |
DB1 | user | user_4 |
DB2 | user | user_5 |
DB2 | user | user_6 |
DB2 | user | user_7 |
DB2 | user | user_8 |
DB2 | user | user_9 |
- 各user_xテーブルの定義
mysql> desc user_0; +-------+-------------+------+-----+---------+-------+ | Field | Type | Null | Key | Default | Extra | +-------+-------------+------+-----+---------+-------+ | id | int(11) | NO | PRI | NULL | | | name | varchar(45) | NO | | NULL | | +-------+-------------+------+-----+---------+-------+
- 投入されているデータ
INSERT INTO user.user_0 VALUES (1000, 'user_1000'); INSERT INTO user.user_5 VALUES (1055, 'user_1055');
設定ファイルの定義
テーブル用の定義は以下のようになります。
https://github.com/takashabe/aurora-sample/blob/master/conf/application.conf
- 抜粋
aurora { table-name-configs { patterns = [ "user_[0-9]" ] } }
リゾルバの追加
https://github.com/takashabe/aurora-sample/blob/master/src/main/scala/com/example/Main.scala#L37
テーブル名を解決するためのリゾルバです。これに目的のuser_idを渡せば、そのデータが入っているテーブル名を取得出来ます。
private val tableNameResolver = new AbstractTableNameResolver[Int]("user") { override protected def getSuffixName(userIdHint: Int): String = { "_" + "%d".format(userIdHint % 10) } } private val auroraTableNameService = AuroraTableNameService(tableNameResolver, new File("./conf/application.conf"))
リゾルバの利用
https://github.com/takashabe/aurora-sample/blob/master/src/main/scala/com/example/Main.scala#L46
val table = auroraTableNameService.resolveByHint(userId).get
リゾルバで取得したテーブルを println
すると以下の様な感じです。
TableName(name = user_1)
ユーザを引いてみる
https://github.com/takashabe/aurora-sample/blob/master/src/main/scala/com/example/Main.scala#L84
println(findByUserId(1000)) println(findByUserId(1055)) println(findByUserId(9999)) // 結果 Success(User(1000,user_1000)) Success(User(1055,user_1055)) Failure(java.io.IOException: entity is not found.)
まとめ
- 普段仕事でauroraの元?となったPHP向けの gree/cascade · GitHub を使っていますが、大体似たような感じで使えそう
- ライブラリ自体ではデータソースとテーブル名の解決を行っているだけなので、他のフレームワークやライブラリと併用する場合も楽そう
- ある程度以上の規模で利用する場合にはリゾルバ用のレイヤを1つ追加して、抽象化してあげると楽かなと思いました
- スケーラビリティは置いといて、複数DBしないのが一番楽だと思います
20日目は taketon_ さんの Finagleでサーバープログラミング です。