クラウドでのデータ設計方法
『日経 SYSTEMS』の2010年10月号から、マイクロソフトの萩原正義さんによる『クラウドの設計セオリー』というコラムが連載されています。
クラウドがこれまでのエンタープライズシステムと比較して技術的な不連続性があることなど、クラウドにまつわる技術的な疑問とその答えが整理された、非常に読み応えのある連載です。
先日、2010年12月号の『クラウドのデータ分析設計法』というコラムを拝見させていただいて、自分の中にあったクラウドでのデータ設計方法の疑問点・不明点をきれいに整理することができました。
その整理できた内容を忘れないよう、備忘録として残しておこうと思います。
※全てを網羅した内容にはなっていません。
私自身が理解し易いようにするために、意図的に記述を省いているところがあります。
1.概要:データ層のパラダイムシフト
現在のエンタープライズシステムで主流となっている、いわゆる「3層システム」(UI層−ビジネスロジック層−データ層)では、データ層に RDBMS を使用しています。
RDBMS は、データを表形式で扱えるという理解容易性がありますが、
スケールアウトがしづらく(今は Oracle RAC とかありますが)、
システムのボトルネックになりがちという問題もあります。
一方クラウドでは、データ層がシステムのボトルネックとならないよう、データ層がスケールアウトできるようになっています。
これを実現するために、KVS などの新しいデータストア技術が利用されています。
つまりクラウドでは、これまでの RDBMS とは異なり、データ層をスケールアウトできるようにデータを設計する必要があります。
2.そもそもの疑問
では、データ層をスケールアウトできるようなデータ設計として、具体的に何をどのようにすれば良いのでしょうか?
良く耳にするのが、以下のような事項です。
- データの非正規化を行う(JOIN ができない・しづらいため)
- ロードバランスを考慮する(データが特定箇所に集中するとボトルネックとなるため)
- システムと近いところにデータを配置する(latency を小さくするため)
なのですが…これらを具体的にどう実現すれば良いのかが分からずに困っていました。
その答えが、上記の連載にまとめられていました。
以下、順に整理していきます。
3.分析:基本は同じ
分析のスタートとして、ユースケースとE−R図を作成するという基本は同じです。
但し、これらの活用方法に、これまでと異なるところがあります。
具体的には、データの垂直・水平方向にどう整理するかを判断するために用います。
具体的に見ていきましょう。
3.1.垂直方向のデータ整理
「垂直方向」とは、表形式をイメージしたときの垂直方向、つまり列レベルでのデータのことです。
クラウドでは、各データが同じサーバにあるとは限らないため、JOIN は避けるべきです。
そのため、JOIN しなくても良いように、データを意図的に非正規化します。
具体的には、E−R図に基づいてデータを「マスタ」と「トランザクション」に分け、
「トランザクション」側に「マスタ」の参照するデータを項目として持たせるようにします。
こうすれば、「マスタ」と「トランザクション」との間で JOIN を発生させずに済むようになります。
「マスタ」のデータが「1箇所1事実」ではなくなってしまいますが、「マスタ」は更新されることが少ないため、
必ずしも「1箇所1事実」でなくても良いということが、この考え方の背景にあります。
3.2.水平方向のデータ整理
「水平方向」とは、表形式をイメージしたときの水平方向、つまり行レベルでのデータのことです。
特定の行(レコード)へのアクセスが集中すると、特定のサーバへの負荷が高くなり、
ひいてはシステム全体のパフォーマンスの劣化につながります。
そのため、使用頻度の高い行(レコード)を、各サーバへ分割配置できるようにします。
具体的には、ユースケースで優先度・使用頻度の高いデータを特定し、
これを各サーバへ分割配置するようにします。
データの分割方法としては、以下のものがあります。
slim3本入手しました
slim3 本を、Amazon で購入しました。
(7月末には書店に出回っていたらしいですが、仕事の都合で書店巡りをする余裕がないので Amazon で予約したら、到着は 8/2 でした。。。)
slim3 と Windows Azure の夢のコラボw
内容の8割以上は、Datastore について記述されています。
実際 slim3 で開発してみると、Controller & JSP はほとんど SAStruts の知識で済むので、詰まるとすれば Datastore の部分なんですよね。
逆に、Web 画面周りで詰まる場合は、slim3 公式サイト & SAStruts 公式サイトにいくと良い感じになると思います。
オープンソース徹底活用Slim3onGoogleAppEngineforJava
- 作者: ひがやすを,小川信一
- 出版社/メーカー: 秀和システム
- 発売日: 2010/07/30
- メディア: 単行本
- 購入: 12人 クリック: 462回
- この商品を含むブログ (36件) を見る
けいおん! ねんどろいど 中野梓 2次出荷分 (ノンスケールABS&PVC塗装済み可動フィギュア)
- 出版社/メーカー: グッドスマイルカンパニー(GOOD SMILE COMPANY)
- 発売日: 2010/06/30
- メディア: おもちゃ&ホビー
- 購入: 17人 クリック: 256回
- この商品を含むブログ (48件) を見る
slim3の書籍の予約開始です
slim3 の書籍の予約が amazon で開始されました。
GAE/J でグローバルトランザクションや EoD を体感したい人は読むべし!
オープンソース徹底活用Slim3onGoogleAppEngineforJava
- 作者: ひがやすを,小川信一
- 出版社/メーカー: 秀和システム
- 発売日: 2010/07/30
- メディア: 単行本
- 購入: 12人 クリック: 462回
- この商品を含むブログ (36件) を見る
restletを使用したWebサービスの構築・公開
GAE から他の Web サービスを呼び出すには、基本的には URL Fetch を使えばOKです。
では、GAE 上で Web サービスを構築・公開するには、一体どうすれば良いのでしょうか?
コチラによると、restlet で実現可能とのことでしたので、試してみることにしました。
ちなみに、restlet を GAE/J で使用する方法については、下記サイトを参考にしました。
http://wiki.restlet.org/docs_2.0/13-restlet/275-restlet/252-restlet.html
また、筆者は REST を「SOAP よりも簡単な Web サービス」という程度でしか知りませんので、REST について至らない点はご容赦下さい。
1.環境設定方法
(1)restlet の「Version 2.0 Snapshot」を、ここからダウンロードします。
- 色々ある Edition のうち、「Edition for Google App Engine」を選択します。
- 「Zip archive」を選択します。
- 「restlet-gae-2.0snapshot.zip」という名前のファイルをダウンロードできます。
(2)ダウンロードした「restlet-gae-2.0snapshot.zip」を、
任意のディレクトリに解凍します。
(3)解凍先ディレクトリ/lib にある、下記の2つの JAR ファイルを、
GAE プロジェクトの war/WEB-INF/lib にコピーします。
- org.restlet.jar
- org.restlet.ext.servlet.jar
(4)上記の2つの JAR ファイルを、クラスパスに追加します。
これで、環境設定は完了です。
2.サービスの構築方法
今回は、アクセスされたら何らかの String を返す、簡単な Web サービスの構築方法を簡単にまとめます。
(1)org.restlet.resource.ServerResource を継承したクラスを作成します。
サーバとして提供したい具体的な処理を、ここに記述します。
提供するメソッドは、getVariants() メソッドをオーバーライドするか、
@Get・@Post アノテーションを付加する形で実装します。
以下は、@Get アノテーションを使用した、注文件数を注文 Service を介して渡す例です。
package xxx.yyy; import org.restlet.resource.Get; import org.restlet.resource.ServerResource; public class OrderResource extends ServerResource { @Get public String represent() { return "注文件数は" + new OrderService().getCount() + "件です。"; } }
(2)org.restlet.Application を継承したクラスを作成します。
外部システムとのやりとりの仕方を、ここに記述します。
今回のように、外部からのアクセスに対して上記(1)の結果を返す場合は、createInboundRoot() メソッドをオーバーライドし、以下の処理を記述します。
- org.restlet.routing.Router のインスタンスを生成します。
- 上記(1)の Resource を、Router に登録します。
- Router を戻り値として返します。
以下は、上記(1)の OrderResource を使えるようにする例です。
package xxx.yyy; import org.restlet.Application; import org.restlet.Restlet; import org.restlet.routing.Router; public class OrderApplication extends Application { @Override public Restlet createInboundRoot() { Router router = new Router(this.getContext()); router.attachDefault(OrderResource.class); return router; } }
(3)web.xml に、org.restlet.ext.servlet.ServerServlet の定義を追加します。
ServerServlet は、restlet 用のサーブレットです。
これに、init-param で上記(2)の Application を登録し、
GAE アプリケーションとして使用できるようにします。
以下の例では、/rest へアクセスすることで、OrderResource#represent() の戻り値を取得できるようになります。
RestletServlet org.restlet.ext.servlet.ServerServlet org.restlet.application xxx.yyy.OrderApplication RestletServlet /rest
3.課題
今回の例は String を返すだけでしたが、
もっと複雑なオブジェクトをやりとりする方法について、
今後検討していきたいと考えています。
GAE/Jの人向きの書籍
先日、幕張をうろうろしていたら、たまたまみつけました。
『すっきりわかる Google App Engine for Java クラウドプログラミング』(中田 秀基さん著)
ちょうど URL フェッチとか OAuth とか XMPP とかを腰を落ち着けて調べようと思っていたところだったので非常にタイムリー!
すっきりわかるGoogle App Engine for Javaクラウドプログラミング
- 作者: 中田秀基
- 出版社/メーカー: ソフトバンククリエイティブ
- 発売日: 2010/07/02
- メディア: 単行本
- 購入: 3人 クリック: 177回
- この商品を含むブログ (15件) を見る
【slim3】日付型のvalidation【Struts】
今更なんですが、日付型データの validation で躓いたのでメモしておきます。
※Struts 由来のものなのですが、slim3 でも同様なので、一応 GAE/J の slim3 カテゴリにしています。
1.具体的な事象
日付型の validation では、"yyyy/MM/dd" などのフォーマットを指定できる dateType を使用するかと思います。
この dateType ですが、
- "2010/01/03" : OK
- "2010/1/3" : OK(前のゼロがなくてもOK)
- "2010/01/32" : NG(存在しない日付)
- "2010/01/a@" : NG(数値ではない)
になるのはわかります。
ですが、"2010/01/3a" と指定するのはOKだったりします。
2.原因
dateType で使用している SimpleDateFormat#parse() が、ParsePosition を指定していないためです。
"2010/01/3a" の "a" 以前までが日付として解釈され、"a" が切り捨てられます。
そのため、今回の場合では、値が "2010/01/3" と「誤認識」されます。
3.考えられる影響
ユーザによっては、この箇所だけではなく、全体的な品質に問題のあるシステムなのではと誤解されるケースもあり得ます。(私の場合、実際そうでした。)
また、validation はOKでも、後続の処理でのデータ変換で想定外の例外が起こる可能性もあります。
4.対処方法
dateType と一緒に、正規表現のチェックも追加すると良いです。
Struts なら mask、slim3 なら regexp を追加します。
※正規表現チェックだけですと、数値部分が日付として妥当かをチェックすることが面倒なので、dateType とセットにした方が無難かと思います。
5.具体例(slim3)
Validators v = new Validators(RequestLocator.get()); v.add("date", v.dateType("yyyy/MM/dd")); v.add("time", v.dateType("HH:mm"));
↓
Validators v = new Validators(RequestLocator.get()); v.add("date", v.dateType("yyyy/MM/dd"), v.regexp("^\\d{4}/\\d{2}/\\d{2}$")); v.add("time", v.dateType("HH:mm"), v.regexp("^\\d{2}:\\d{2}$"));