双六工場日誌

平凡な日常を淡々と綴ります。

データ分析基盤の「Scheme on Write」から「Scheme on Read」への転換についてのメモ(暫定版)

最近、データ分析基盤を設計するに当たっての考え方を整理する必要があって、要件に応じたデータ分析基盤を設計するに当たっての基本的な考え方は何かを考えています。

近年、データ分析基盤はデータ収集ソフトウェア、データストア、分析処理、分析処理の利用方法などなど、検討すべき項目が多くなり、この辺りの技術の流れを把握している人は当然のように設計を行うことができますが、そうでない人に取っては、どこから検討を始めるべきかわかりにくくなっているように思います。

自分の中でも、これがうまく言語化できておらず人に教えようと思っても、結構詰まってしまうので、ブログで言語化してみる次第です。

最近のデータ分析基盤の基本は「Scheme on Read」型のアーキテクチャ

最近のインターネット上で見るデータ分析のアーキテクチャは、データをデータソースから収集したのち、最低限の加工(ログをフィールドごとに区切るなど)をHDFSやS3、Azure BLOB ストレージなどに置くことが多くになっていると思います。

Hadoopが普及し、分散処理を利用するハードルが下がったことで、一度データを保存してしまえば、あとからデータを分析できる形式に整形することができるようになったことが、このようなアーキテクチャを可能にしていると思います。

保存したデータは、あとでHadoopを使って分析に使いやすい形に加工し、その上で独自の可視化アプリなり、Tableauなどのセルフサービス側のBIツールで読み込んで使うことになります。

最近のデータ分析基盤系のスライドを見ると、このアーキテクチャを前提に以下の4つのコンポーネントに分けて、機能が語られていることが多いようです。最近のデータ分析関連のプロダクトは、このような区分で作られていることが多いので、自分としてもしっくりきます。

このアーキテクチャは「Scheme on Read」という言葉で呼ばれることがあります。データ加工を出来る限りあとのフェーズにすることで以下のメリットを得ることができます。

  • テキストや画像、音声、動画等の非構造化データもそのまま同じ仕組みで保存可能
  • データの種類や分析内容が変わっても、「処理」フェーズ以降で必要に応じてデータを加工することで柔軟に対応できる
  • データ保存にはファイルをそのまま保存できるようなストレージや分散ファイルシステムを使うことで、データの収集・保存の仕組みをシンプルに保つことができる

分析対象と分析手法が多様化していることから、このような特徴はデータ分析基盤の大きなメリットです。

それでは「Scheme on Write」の基盤とは?

Hadoopが普及する以前は、データ分析基盤と言えば、データ・ウェアハウスを中心とするBIシステムのことを指していました。ただ、BIシステムとHadoopを利用したデータ分析基盤のアーキテクチャの比較は、私が知る限りあまりないように思います。

データ・ウェアハウスのアーキテクチャは「Scheme on Read」と比べると、「Scheme on Write」、つまりデータを保存する際にスキーマを与えるという設計の方法論と考えることができると思います。

BIシステムの構成要素をざっくりまとめると、以下のようになっています。

  • BIシステムの構成要素
    • ETL
      • Extract: 業務システムからのデータ抽出・収集、Transform: データ加工、Load: データ・ウェアハウスへのデータロード
    • 蓄積
      • データ・ウェアハウスへの蓄積
    • 分析・ビジュアライズ
      • BIツールでの分析

先ほどの最近のコンポーネント分割と比べると、Iシステムではデータ収集とデータ加工が「蓄積」の前に来ており、データ蓄積の時点で分析に必要な前処理がひと通り終わっていることが前提となっていることが大きく異なっています。

データ・ウェアハウスは、単にデータを長期間に渡って蓄積するだけではなく、データ分析の目的に特化したテーブル構造を取ることで、複数の軸でデータの集計・分析を高速に行うことができるようになっています。

データ・ウェアハウスで用いられるテーブル構造は「スタースキーマ」と言われ、各種マスタ情報やデータ集計対象期間等の属性情報を格納した「ディメンション・テーブル」と取引履歴などの履歴データをディメンションテーブルの軸で切って格納した「ファクト・テーブル」からできており、通常は業務システムとは別のテーブル構造を持っています。この構造にしたがってテーブル設計を行い、データを格納することで、BIツールから効率的にデータ分析ができるように工夫がなされています。

そのため、データ・ウェアハウスにデータを蓄積する際には、そのテーブル構造にデータを合わせるためのデータ加工や不正なデータやフォーマットが合わないデータを弾くためのクレンジング処理も必須です。しかし、一旦データを入れてしまえば、後段の分析では前処理が終わった状態でデータを使うことができる、というのがこのアーキテクチャの特徴です。

上で見てきたデータ・ウェアハウスを中心としたBIシステムは、その特徴故にデータ投入時の検討事項が多くなり、かつ、事前のテーブル設計で漏れがあったり、新たに分析対象となる軸が出たりした場合は、またテーブル設計からやり直す必要が出てきます。そのため、最初の要件定義の際に分析軸まで決められるようなデータの種類が増えない業務システムであればいいですが、そうでない場合は設計が困難です。

BIシステムの設計は、企業内部に閉じていることが多く、設計自体が各企業のノウハウを含むので、Web上では概論を超えるような設計内容があまり公開されていないように思います。書籍としては、以下の書籍が参考になりました。

BIシステム構築実践入門 (DB SELECTION)
平井 明夫
翔泳社
売り上げランキング: 55,350
BIシステム構築実践入門 eコマースデータ活用編 (DB Magazine SELECTION)
平井 明夫 梶 幸司 野澤 ひろみ 佐藤 宏樹 深井 淳 駒原 雄祐
翔泳社
売り上げランキング: 510,026

Scheme on Write」は「Scheme on Read」に乗り越えられたと言えるか?

Scheme on Write」はデータ分析基盤が世の中になかった時に、業務システムとデータ分析を行うシステム(情報系システム)を分割し、業務にしか使われていなかったデータを分析用のデータに転換することでビジネス上の価値を生み出すことを意図して考えだされた仕組みです。

一方、「Scheme on Read」は、上記の背景から生まれた既存のBIシステムが近年のデータ増加のために行き詰まってくる中、Hadoopによる大量データの分散処理が一般的になったことから形作られてきたアーキテクチャだと思います。

ただ、後者が前者を単純に乗り越えて、後者だけですべてが成り立つ世界になったかというとそうでもないかなと思います。どちらにしても、最終的にデータ分析を行うためには、データ分析の目的に応じてデータを整形し、分析する必要があり、また、データの前処理は後に回せば回すほど、余分なデータをあとに持ち越すことになるため、処理のオーバーヘッドは増えてきます。

・・・この関係をうまく整理できればいいのですが、今日の時点ではまだまとまっていません。 連休中に考えがまとめられたらと思いますが、なんとかなるかなー

長くなったので、尻切れですが、今日はここまで。続きはまた考えがまとまり次第ということで。

--

追記

メモの続き書きました。このメモはアプローチの整理までで一旦終了で。

データ分析基盤の「Scheme on Write」から「Scheme on Read」への転換についてのメモ(暫定版)・続き - 双六工場日誌

【まとめ】エンジニアサポートCROSS 2015 に参加してきた #cross2015

すでに会場で書いた分を速報としてブログにもアップしていますが、1/29 に行われた「エンジニアサポートCROSS 2015」に参加してきました。

f:id:sechiro:20150129200224j:plain

  • 会場前に停泊していた「ASUKA 2」

CROSSは、今年で4回目になるイベントで、様々な分野のエンジニアやコミュニティが一堂に介して交流するイベントです。毎年年明けすぐにやっているので、東京で活動するIT勉強会の合同新年会みたいな面もあったり。

いろいろな人が集まるので、毎年新しい発見があり、また、しばらく会っていなかった人との旧交を温めたり、個人的にはWebエンジニア版のコミケ*1のような位置づけになっています。

去年までは、新宿ベルサールでの開催だったのですが、今年は場所を変えて横浜の「横浜港大さん大ホール」での開催。主催のところをよく見ると、今年は「横浜市経済局」とか「一般社団法人 神奈川県情報サービス産業協会」とか地元とかの地元の団体も後援に入っていました。

参加したセッションのうち、最初の2つは会場にいるうちに速報だけ書いていたので、そちら参照。こういうのは、あとになればなるほど億劫になって、かつ、記憶が薄れて書きにくくなってしまうので、サクッとやってしまうのが大事ですね。

【速報】CROSS2015「女子大生UXデザイン概論」に参加してきたので、偏った視点でメモ - 双六工場日誌

【速報】CROSS2015「Webアプリケーションから機械学習まで ~ PythonとPythonコミュニティの2015年大展望」のメモ - 双六工場日誌

このエントリは、上記のエントリの続き兼参加報告まとめのエントリです。

Twitter の #cross2015 のハッシュタグを追っていると、続々と参加ブログが増えていますが、そちらについては公式のまとめページができるようです。ページができたら、そのURLを追記します。

*1:コミケに継続的にサークル参加していると、コミケは新しい本を買うと同時に、知り合いの近況を聞いたりする社交の場みたいになるんですよね

続きを読む

【速報】CROSS2015「Webアプリケーションから機械学習まで ~ PythonとPythonコミュニティの2015年大展望」のメモ

f:id:sechiro:20150129140056j:plain

CROSS 2015、ランチセッションのあとは、Webアプリケーションから機械学習まで ~ PythonとPythonコミュニティの2015年大展望に参加してきました。

すでにTogetterがまとめられていたので、ツイートはこちら。

CROSS2015「Webアプリケーションから機械学習まで ~ PythonとPythonコミュニティの2015年大展望」 - Togetter

野球クラスタ(仮)の話だけはすでにスライドが上がっていました。

Python野球クラスタの紹介

セッションの内容は、日本国内のPythonコミュニティの網羅的な紹介でした。Pythonを仕事で使っているものの、これまでPythonコミュニティに参加したことがなかったので、それをまとめて聞けたことが個人的には収穫です。

PyLadiesという女性Python使いの国際的なコミュニティがあって、その日本国内のコミュニティの立ちあげを今年新卒で就職したデータサイエンティストの女性がやっているというのが衝撃でした。

f:id:sechiro:20150129142249j:plain

f:id:sechiro:20150129142312j:plain

登壇者と、それぞれが属しているコミュニティは、セッションページを参照。

ビールの時間が来たので、一旦ここまでで。

【速報】CROSS2015「女子大生UXデザイン概論」に参加してきたので、偏った視点でメモ

Crossの会場は、大さん橋ふ頭の最先端にある「大さん橋ホール」。

海や客船が見られたのにはテンションが上がりましたが、駅から遠くて、海風が冷たかった。

f:id:sechiro:20150129110506j:plain

  • たまたま停泊していた客船

f:id:sechiro:20150129110513j:plain

  • ふ頭の先端に至る果てしない道

「女子大生UXデザイン概論」の内容

セッションが終わった直後ですが、速報的にメモってみます。

正直、タイトルに釣られていったので、何の企画かよくわかりませんでしたが、女子大生とエンジニアの生活を比較して、視点の違いを考えてみようという建前の企画でした。そのほかのところは会場限定で。

続きを読む

EBSボリュームにアタッチされているEC2インスタンスと同じタグを付けて、「Cost Explorer」や「Resource Group」でのグループ化を助けるスクリプトを書いた

昨年AWSから発表された「Cost Explorer」と「Resource Group」の機能を活用するに当たって、こういう需要はそこそこあると思ったので、表題の通りの簡単なスクリプトのメモっておきます。

前回のエントリ前々回のエントリの実利用例になっています。

ただ、このスクリプトではプロセスフォークのコストよりも、AWS CLIの実行コストの方が全体に占める割合が圧倒的に大きいので、手早く書ける以上にあえてsetでリソースを節約する理由はそれほどないですね。。。*1

前々回エントリ「AWS CLIとjqを使って、AWSのELBボリュームがアタッチされているEC2インスタンス名を出力するワンライナーを書いた - 双六工場日誌」では、EC2インスタンスから「Name」タグを抜き出ししていますが、今回はAWSの「Cost Explorer」のサンプルにある「Cost Center」を抽出対象のタグとしたいと思います。また、インスタンスIDは結果の正しさを確認するためには必要ですが、タグ付けには不要なので、このスクリプトでは省略し、EBSのボリュームIDだけ抜き出します。

そして、それと同じタグをEBSにもつけて、Cost Explorerで、EBSも含めたEC2コストも見られるようにするという寸法です。また、EC2インスタンスにはすでに「Cost Center」のタグが付いていて、Cost Explorer上で、そのタグ別集計の機能が有効化されていることを前提とします。こちらの設定方法はAWSのドキュメントに書かれているので、ここでは割愛。

前置きが長くなりましたが、スクリプト本体は以下の通り。

#!/bin/bash
set -ue

tag_list=`aws ec2 describe-instances | jq -r '.Reservations[].Instances[] | .BlockDeviceMappings[].Ebs.VolumeId + " " + ( .Tags[] | select(.Key == "Cost Center")|.Value )'`

echo "$tag_list" | while read line
do
    set -- $line
    aws ec2 create-tags --resources $1 --tags Key="Cost Center",Value=$2
done

たったこれだけですが、これでインスタンス作成時にタグを付けておけば、それと関連するボリュームをまとめてみたり、コストを集約したりできるようになります。「Resource Group」と一緒に「Tag Editor」機能も追加されていますが、EBSのボリュームを一つひとつつけるのはちょっと手間なんですよね。

上記では同期するタグは固定値にしましたが、ユースケースによっては引数にして、同期するタグを実行時に決めてもいいかなと思います。ただ、その場合は途中の"set --"でpositional parameterが書き換わってしまうので、スクリプトの最初で引数を別の変数に入れておく必要があるので、その点はご注意ください。

今日はこの辺で。

*1:プロセスフォークで死ぬ例で出せそうなものを考えてみましたが、プロセスフォークが多くなるスクリプトはそもそもデータの使い方の時点でダメなところが多いので、変数を分解するところだけでわかりやすくするものがうまくできず…

シェルスクリプトの中で1行ずつ変数を分割する際には、cutとかawkとか余計なプロセスを起動せずsetを使って分割した方が効率的

シェルスクリプトの中で、スペース区切りもしくはタブ区切りのレコードを扱うことがよくあると思います。

たとえば、前回のエントリ「AWS CLIとjqを使って、AWSのELBボリュームがアタッチされているEC2インスタンス名を出力するワンライナーを書いた - 双六工場日誌」のスクリプトの出力は以下のようになります。

i-ec56a9f5 vol-07d00601 servername

i-ec56a9f5 vol-8f550991 servername

このようなレコードの特定の列を取り出して、処理する際にどうするのが効率的か、というのがこのエントリのお題です。

非常に古い話題なので、昔からシェルスクリプトを書いている人には自明な話ではありますが、最近、シェルの標準機能の話を聞く機会がなく、失われつつある技術になってきている気がしているので、改めて確認ということで。

例として挙げたレコードから最初の1列目(インスタンスID)だけ取り出したい時、たとえばこんな方法があります。

line="i-ec56a9f5 vol-07d00601 servername"

instance_id=`echo $line | cut -d' ' -f1`
volome_id=`echo $line | cut -d' ' -f2`
instance_name=`echo $line | cut -d' ' -f3`

こうするとフィールドの切り出しごとに新しく"cut"のプロセスが起動されるため、ワンライナーや実行回数の少ないバッチ処理用のスクリプトの場合はいいのですが、監視スクリプトのような一定の頻度で呼び出されるものは、その差がボディーブローのように効いてくることがあります。

このようにプロセスフォークのコストが無視できない場合は(下線部追記)、シェルスクリプト内で各フィールドの値を使う場合は以下のように"set"で分割しましょう。

line="i-ec56a9f5 vol-07d00601 servername"

set -- $line
instance_id=$1
volome_id=$2
instance_name=$3

ここで使っている$1、$2、$3といった変数は"the positional parameters"と言われるもので、シェルスクリプトでは、スクリプトや関数の引数を参照する際に使っていることが多いと思います。この変数は"set --"のあとに、スクリプトの引数のようなイメージで、空白区切りの文字列を入れるとシェル内で再設定することができます。(シェルスクリプトの引数として与えていた値は消去されます)

この2つで、どれだけの差が出るのか、以下のような簡単なベンチマークを取ってみました。実行環境は、僕のMBA 2012です。

続きを読む

AWS CLIとjqを使って、AWSのELBボリュームがアタッチされているEC2インスタンス名を出力するワンライナーを書いた

件名の通りのAWSのELBボリュームがアタッチされているEC2インスタンス名を、AWS CLIで取得しようと思ったら、ちょっと手間取ったので、その結果をワンライナーにしたメモ。

結果はこちらです。こういうワンライナーになったのは、"join"コマンドを使ってみたかったという雑念も半分ぐらいありますね。

join <(aws ec2 describe-volumes | jq -r '.Volumes[].Attachments[] | .InstanceId + " " + .VolumeId' | sort) <(aws ec2 describe-instances | jq -r '.Reservations[].Instances[] | .InstanceId + " " + ( .Tags[] | select(.Key == "Name")|.Value )' | sort) 

"join"コマンドを使わない別解はこちら。上は、ボリュームの情報から撮り始めてしまっていましたが、EC2の構成情報を見たら、そちらに必要な情報がすべて入っていたので、上のようにトリッキーなことをしなくても大丈夫でした。

aws ec2 describe-instances | jq -r '.Reservations[].Instances[] | .InstanceId + " " + .BlockDeviceMappings[].Ebs.VolumeId + " " + ( .Tags[] | select(.Key == "Name")|.Value )'

実行の際には、AWS CLIとjqが必要なので、事前にインストールして、AWS CLIからAWSの適切なリージョンにアクセスできるようにセットアップまで済ませておいてください。AWS CLI自体のセットアップ方法は、いろいろなブログに書いてあると思うので、ここでは割愛します。

結論はここまで。以下は、詳細説明です。

続きを読む