ひしだまの変更履歴

ひしだまHPの更新履歴。
主にTRPGリプレイの元ネタ集、プログラミング技術メモと自作ソフト、好きなゲームや音楽です。

第5回Hadoop座談会の感想

2011-06-29 23:59:54 | PG(分散処理)

Hadoop座談会(第5回)に参加してきました。ありがとうございました。
Togetter: 2011/06/29_Hadoop~座談会(第5回) #hadoopmodeling
johtaniさんのブログtorazukaさんのブログ(虎塚)落合さんのブログgggggoroさんのブログ

今回はなんと参加者300人近くということで、原宿の会場。奥行きが深い構造で、ぎっしり人が入っていて、びっくりした。この広さでプロジェクター2つというのは、少々厳しかったのではないかという気もする^^;
今回は他の人のブログとかが充実しているので、自分は軽く感想をまとめるだけにしておきまする。(つーかプロジェクターが見えづらかったので、あまりメモできてない(汗) 下手に頭を動かすと、後ろの人の迷惑になるだろうし…)

話の題材は、鉄道会社と電力会社。
どちらもよく知られた社会インフラで、システムが稼動したら耐用年数が長い(10年以上)。事前検証なんかも数年かける。
自分が携わってきたのとは結構違うなぁ。


鉄道の方では運行に関する(舞台裏の)話が面白かった。山手線や新幹線って、線路脇に信号無いんだ。ほほー。

Hadoopの話は直接は出てこなかったけれど、分散技術が使えそうなものとして、可用性(連続稼動)とバッチ処理の高速化が挙げられていた。
連続稼動って、フォールトトレランス(FT)やActive-Activeといった冗長構成の関連らしい。Hadoopも、スレーブノードが一部壊れても自動的にリカバリーするから、使えるかもしれないということかな?

鉄道ってシステム化が進んでいそうなのに、そうでもない分野もあるっていうのは確かに意外だったかも。


九州電力の方は、事例として名前が挙がりつつも詳細が載っていなかったので、気になっていたところ。

平成21年度(2009年度)から分散処理・仮想化の検証を始め、その一環でHadoopも検証したらしい。
仮想化基盤にはKVMを使用。VMwareとベンチマークを比較して、多少劣るものの大差ないという判断。
Hadoopは物理マシン10台の上に仮想サーバー110台。仮想マシンのコア数を変えてみたりとか、色々試したそうだ。

平成22年度(2010年度)は本番バッチと同様のものをHadoopで作ってみて比較。王道だ(笑)
電柱の巡回をする為のデータ作成の月次バッチ(電柱は100万本)だそうで、
Oracleバッチが7多重で17時間20分かかっていたところを、Hadoop仮想サーバー10台(物理マシン2台)+MySQLで32分。43ジョブに分割して、並列実行できるところは並列化したらしい。
他にももうちょっと詳しくスペックを言っていたけれどメモできなかったので、資料が公開されると嬉しいな~。
MapReduceの製造には2名のエンジニアが2ヶ月かけて死にそうだったそうで、ハードルは高い。

平成23年度(2011年度・今年)はAsakusaフレームワークに挑戦。
素のMapReduceを作るよりも効率よくなるはずとのこと(1/3とか)。
他にも部署名変更で9時間×60日かかっているシステムがあるので、それをやってみるとか。

将来への展望としては、ひとつにはスマートグリッド。
メーターが800万台あるので、スマートメーターへの交換だけで10年はかかる。
30分に1回データを取得するので、1時間に2回。1ヶ月で2×24×30日×800万台のデータを扱うことになる。また、交換は徐々に行われるので、最初は少ないけれどもだんだん大きくなってくる。(クラウド向きだなぁw)

ここで自分が疑問に思っていたことが会場からの質問でも出て、御徒町さんから補足が。
検証を仮想マシンでやっていたようだが、Hadoopは物理マシンでないと性能が出ないのではないかという懸念。「Hadoopは仮想化の流れに逆行するのか?」なんて話もあったし。
で、Hadoopで重要なのはディスクI/Oなので、仮想化で共有ストレージ(NetAppやEMC)を使っていると残念な結果になるが、ディスク分散していれば仮想環境でもHadoopはスケールするということのようだ。
仮想化の方式なんて全然詳しくないので、これは良いことを聞いた。

あと、monkey magicが8月にOSS化するのだそうだ。これまた思い切ったなぁ。でもそうすると、AsakusaFW+monkey magicの組み合わせが鉄板になるかな?(笑)


『Scala実践プログラミング』

2011-06-28 23:02:29 | PG(Scala)

Scala実践プログラミング』を一通り読み終わった。

Scalaの初心者がステップアップするには、そしてJavaからScalaへの移行を考えるにはとても良い本だと思う。
上級者にとっても、デザインパターンとか継続とか、他のScala本ではあまり見られない(と思う)内容は興味深いのではないかな?


まずはさらっと1章でインストールと簡単な使い方、2章で基本的な文法のおさらい。基本なので他のScala本でも触れられている事だけれども、けっこう色々網羅されているし、「あれ、こんなのも出来たんだ!」という発見もある。ただ、みっちり詰まっているので、Scalaを全く知らない人には少々厳しい気もする^^;

で、3章から徐々に本領発揮。関数型言語の特徴について。
4章、ちょっとだけだけど他言語との比較。Javaとの比較はよく聞くけれど、HaskellやらOCamlやらとの比較は初めて見た。コーディング例も載ってて、まぁ見慣れてないから違和感あるわな^^; 頭も硬くなってきてるし、やっぱり慣れているALGOL系(C言語やJava)に近いScalaが分かりやすいやw

で、5章・6章・8章のテーマが良い。この辺りがタイトルの「実践」をイメージしている感じ。
JavaからScalaへの移行を考えると、当然現在Javaで行っている業務・やり方がScalaでどうなるのかが気になるだろう。コンパイル方法・IDE・コーディング規約・テスト方法・Webフレームワーク、そういったものに触れられている。実際に業務に使うなら、どれも重要なポイント。
(自分は最近Scalaの勉強をしてきたけれど、「Scalaの文法」を勉強してきたのであって、実際的な使い方はあまり考えてなかったなーと感じた)
(しかし、IntelliJが見事にスルーされているのは何故だ!?w)
それぞれ文量が少ないのが惜しいけれど、まぁそこを深堀りしていったら、別の本が書けちゃうだろうな^^;
9章のScalaとJavaの連携も、実際に業務で使うときには気にかけなければならないポイント。

7章のデザインパターン、目の付け所が面白い。Javaのデザインパターンの置き換えと、Scala独特のパターン。(自分はJavaのデザインパターンもあまり理解してないんだけどね(爆))
7-2-6『Generalized Type Constraints』(これ、日本語では何て言うんだろう?)で、「<:<」の使い方が初めて理解できた!
Builderパターンとか、格好よすぎるw 値のコピー部分が似たソースになっているので、ここを統一できればもっと良さげ。case classのcopyメソッドを使えばいけるか…?

10章がライブラリーの紹介ということで、Actorと継続。Actorは他でもよく紹介されているけれども、継続はあまり無い気がする。(継続はやっぱり難しい(苦笑) 使い方だけはなんとなくイメージできた、といったところか…。しかも実際に使われてたら、ソースを見たときに動作を絶対誤解しそうだ。Scalaが仕事で使われだしたら、コーディング規約で真っ先に制限されそう(爆))
ライブラリーという題だったので、STMとかAkkaとかscalazとか(聞いたことはあるけどよく知らないもの)も採り上げられていればなーと思ったけれども、標準に入ってない(?)ライブラリーの紹介は雑誌とかの方が向いているかな。

最後、Appendix(よく使うメソッドの一覧)も隠れた高評価だと思う。
仕事でJavaを使う初心者によく言う事があって、それは「StringやList・Map等のよく使うクラスは、API(メソッド一覧)を全部見ておけ!」ということ。初心者は自分が知ってる数少ないメソッドを駆使して事を成し遂げようとするんだけど、(努力するのは良い事だけど、コードが膨らんで見通しが悪くなったりバグが入ったりするので、)提供されている機能を知って、それを使うべき。
Scalaの場合、特にコレクションのメソッドがやたら多くてさすがに大変なので^^;、「まずはこの本のAppendixを全部見とけ」って一言で言えるのはとても良いと思う(笑)


第2回Asakusaソースコードリーディングのメモ

2011-06-24 23:59:51 | PG(分散処理)

Asakusaソースコードリーディング(第二回)、ThunderGate編のメモです。
資料: ThunderGate
Togetter:Asakusaソースコードリーディング(第二回)

ThunderGateはHDFSと外部のRDB(今のところはMySQL)とのデータ転送を行う部分。Asakusaフレームワークが「トランザクションをサポート」と言っているのは、ThunderGateにかかっている。
やはりHadoopを使う上ではHDFSと外部とのデータ配信がひとつのポイントになるので、重要。

説明してくださったのは、ThunderGateのアーキテクチャー設計者の埋金さん。
テレメータ(社会インフラ)の開発だとか、RDBでHAクラスターを組んだりとか、UMLaut/J-XML(XML-EDI)を担当してたりしたそう。
(UMLautはウルシステムズさんのJ2EE・Strutsベースのフレームワーク。ふふふ、触ったことあるから知ってますよ…)


ThunderGateの思想としては、RDBがデータ永続層で、HDFSは作業場。

HDFSはNameNodeがSPoFだし、バックアップ/リストアのノウハウが不足。障害が起きた時の復旧が、Hadoopに詳しい人なら復旧できるのかもしれないけれども、実運用に入った際の運用担当者が復旧できるとは限らない(というかまず無理)。
KVSも未知数だし、RDBMSは信頼性が高いので、こういう仕組みになった。

ThunderGateにおけるRDBMSとHDFSの関係は、
RDB→HDFS: インポート
HDFS→RDB: エクスポート
HDFSがおかしくなったら、HDFSをフォーマットして最初から実行する(データ本体はRDBに永続化されているから)。

ThunderGateの制限として、オンラインや他のAsakusaバッチとは排他する。
ThunderGate自身はスケールアウトしないが、数GB程度のデータであれば大丈夫。数百GBは今後の課題。TB級にはフィットしない。TBは無理でもGBは保証しようという考え方になっている。
PBともなると、他の方法を採る必要があるだろう。

対象RDBMSは、OSSということで商用RDBMSでなくMySQLを選択。MySQLとOracleとSybaseを検証しており、MySQLでも大丈夫という判断。
ただ、やはりOracleで使えるようにして欲しいという要望は出ているそう。(僕も自分が担当していた業務がOracleを使っていたので、Oracleを想定してた)


ThunderGateの使用イメージは、企業VLANにRDB(MySQL)を置き、ファイアーウォールをはさんでHadoopクラスターVLANとつなぐ。
MySQLからデータを取り出すのがImporter、それをHDFSに書き込むのがExtractor。
逆にHDFSから取り出すのがCollector、MySQLへ取り込むのがExporter。
(Importer→Extractor間とCollector→Exporter間は、sshでデータ転送している)
障害があったときにデータを戻すのがRecoverer。
最後にHDFSからファイルを消すのがCleaner。
基本的に、各処理(プロセス)はMonkeyMagicから起動される。

HDFSのファイルはテンポラリー的な位置付けなので、理屈上は、そのファイルを使うバッチが終了したら、ファイルを消してもよい。ただし、別ジョブがそのファイルを使うかもしれない(使わないという判断をするのは難しい)し、障害発生時のリカバリーにも使えるので、残している。

RDBへのアクセスに関しては、標準SQL・JDBC標準APIを使って検証したそうだが、Oracleは性能が良かったがMySQL・Sybaseは遅かったらしい。
そこで、MySQLへのアクセスでは、MySQLのロード文とかを使っている。


Importer(MySQL→HDFS)は、

  1. ロック取得
  2. MySQLからselect into outfileでTSVファイル作成
  3. TSVをzipにしてExtractorへ転送(100Mbpsでは効果があったが1Gbpsではオーバーヘッドの方が大きかった為、デフォルトではzip圧縮はオフになっている)
  4. TSVをSequenceFileに変換してHDFSへ格納(SequenceFileの作成をDBサーバー側で行っても大差無い)

Extractorが分散構造になっていないのは、分散してもDB側は1つなのでボトルネックになる為。
Exporter(HDFS→MySQL)は、概ねwImporterの逆。

  1. CollectorがHDFSのSequenceFileをTSV化、zipにしてExporterへ転送(Ashigelコンパイラによるメタデータが使用される)
  2. DBサーバー上でTSVファイル生成
  3. MySQL上のワークテーブルへロード
  4. ワークテーブルから対象テーブルへコピー(このとき、重複チェックも実施)
  5. ロック解除

ワークテーブルを使っている箇所だけ、Importer/Exporterは対称になっていない。

ロックは、いわばレコードロック。ImporterではWHERE条件によって転送対象を絞る想定なので、転送対象(=処理対象)をロックしておき、他ジョブで更新できないようにするという考えらしい。
このロックは、ロック用テーブルのフラグ項目の更新で行う。(RDBMSのロック機構を使っているわけではない。長時間のロックになる可能性がある為)
テーブルロックのような仕組みも用意されているが、たぶん使わないんだそうで^^;
また、楽観的ロックも使用可能。(更新時刻だかバージョン番号だかを比較に使用)
既にロックされている場合にどう動くかは、実行する際に指定する(3通り)。

処理対象レコードが1万件あったら、ロックテーブルにも1万件のレコードが作られることになり、そのフラグ項目更新となると、けっこうコストが高い。処理対象テーブル1つに対しロック用テーブルも1つ用意されるので、対象テーブルが100個あったらロックテーブルも100個存在することになる。
したがってロック有無の調査をするのも大変なので、IMPORT_RECORD_LOCKというテーブルが調査用に用意されている。

ロールバック/ロールフォワード
RDBへ結果を反映させるとき、直接テーブルを更新せずワークテーブルに一旦入れている理由は、1トランザクションで大量レコード入れるとパフォーマンスが悪いから。
ワークテーブルへは(メモリーに入る単位で)複数回のトランザクションに分けて入れる。
ワークテーブルへ入れている最中に失敗したら、ワークテーブルを消してやり直し。(ロールバック)
ワークテーブルに入れた後であれば、ロールフォワード。

また、オートインクリメントのあるテーブルだと、もし直接更新していたら再実行時に不整合になる可能性もあるので、ワークテーブルを使用している。
(これらの理由でワークテーブルを使うことになったので、ついでに重複チェックも行うようにした)

そういった処理も行っている為、ExportはImportの3倍くらいの時間がかかっている。
1~2GBのデータであればそんなにかからないが、TBくらい来たらアウトかも。
実績値としては、Importが20MB/s。MySQLのロード/dumpが30~70MB/s、SSH転送が50MB/s、ギガビットイーサは100MB/s。Importはいずれにも達していないので、遅い。
高速化案としては、

  • 並列化できる箇所もある
  • Sqoopだとダンプファイルを使わず直接Streamに書いているようだが、標準APIでは無理かも
  • SSHをやめる(認証はともかく、データ転送まで暗号化する必要は無いのでは?という考え)
  • bzip圧縮だと遅かったが、LZOだといいかも?(未検証)
  • 転送を二重化
  • DBサーバーをHDFSクライアントとして直接使う
  • 複数ノードからHDFSへ転送(現状ではDBが遅いので1ノードから転送している)
  • ExporterのSQL実行の並列化(ただし、Oracleは4並列が速かったが、MySQLは並列なしが速かった)

ただし、これで数倍の速度になったとしても、もっと大きいデータになったら焼け石に水なので、他の方法を検討する必要あり。

  • キャッシュ使用。バッチの実行までにHDFSにデータを置くようにする。SequenceFileを差分更新するとか、HBaseを使うとか。ただしトランザクション(ロールバック?)とは相性が悪い。
  • MySQLがボトルネックになるなら、MySQLインスタンスを増やす(レプリカ)とか。レプリカ作成の速度や完全な複製であることの保証・壊れたときの復旧時間などが懸念事項(レプリカなら、壊れても縮退する(遅くなる)だけかも)
  • シャーディングはトランザクションが面倒?
  • データの種類に応じて転送方法を変える。マスターやトランザクションはRDB→ThunderGate→HDFS、変更されないデータはThunderGate以外からロード(例えば過去ログをテープからHDFSへ直接ロード)
  • 全部RDBに置くのはもったいないので、例外的なデータ・使用しそうにないデータは消したらどうか?→不要そうでも消せない→例外処理のハンドリングをきっちり作る必要がある→データを消すのとプログラムを作っておくのと、どちらのコストが高いか?

SqoopはRDBが複数あるなら速いらしい。
AsakusaがSqoopを採用しなかった理由は、トランザクションが出来ない事と、HadoopからDBへのアクセスが出来ない事。
ThunderGateはロックするサーバーが1つだけという制約があり、複数のThunderGateを実行することは出来ない。Exporterもトランザクションがあるので、1つしか動かせない。


ちょっと休憩をはさんで、ここから本当にソースリーディング(笑)

Oracle対応(MySQLをOracleに置き換える)、HBase対応(DBサーバー側あるいはHadoop側にHBaseを置く)をするとしたらどこを変更する必要があるかを念頭に。

各プログラムの構造は同じような感じなので、分かりやすい。

Oracle対応としては、ImporterのダンプSQLをOracle用のSQLに変え、ResultSetでループしてファイルへ書き出すようにする。ExporterもSQL修正。MySQLのオートインクリメントはOracleの機能を使うよう変更要。もし実行速度が遅いなら、各DBMSの機能を生かすよう修正が必要。(SQL*Loaderをプロセス起動するとか)

SSH転送部分は面白い構造になっていた。
ImporterからSSH経由でリモートでHadoopクラスター上のExtractorプロセスを起動。パイプラインを使ってzipデータを転送している。つまり一旦zipファイルを作っているのではなく、Streamを使って、Importerの標準出力にzipデータを出力し、Extractorの標準入力からそのzipデータを受け取っている。(したがって、デバッグ用でも、うかつにSystem.out.printlnで出力するとzipデータが壊れる事に!^^;)
なお、DBサーバーの方がセキュリティーが強いので、プロセスはDBサーバー側から起動する。(Collectorも、Exporterから起動)

分散処理プログラムではログ出力がボトルネックになったりすることがあるようだが、ThunderGateでは特別なことはしていない。データ量が増えてもログ出力量が増えるわけでもないので。

また、ワークテーブルは自動的に毎回作られる。

ちなみにソースを見ていると面白いのが色々あって、

  • zipの圧縮レベル設定がコメントアウトされていた。この状態で検証して大丈夫?^^;(ちなみにSTORED(無圧縮)が実際に使用されているのは初めて見た)
  • 「TODO マルチスレッド化」→別プログラムでやってみたけど効果が無かったから、このTODO部分では実装しなかったらしい
  • close()の例外をキャッチしたブロックが、コメント「ここで例外が発生した場合は握りつぶす」とe.printStackTrace()
  • 例外用の定数名がEXCEPRION(エクセプリオン、格好いい!今度どこかで使おうかな(爆))

close()での例外握りつぶしは自分もよくそういう実装をするけれども、例えばzip解凍のclose()の中でCRCチェックをやって例外が出るとかなっていたら、握りつぶすのは良くないだろうなぁ。少なくともログは出さないとね。
tryブロックで例外が発生していたらその例外にclose時の例外も追加、とか出来ると格好いいんだけどな(笑)

そういえば後から気付いたんだけど、ThunderGateが使用する管理用テーブルや各プロセスの引数にMonkeyMagicのIDが使われているようだけど、MonkeyMagicが無いと動かせないのかな…?


今回は割と機能がイメージしやすかった為か、分かりやすかったです。
ありがとうございました。

P.S.
今回は帰りの電車が止まってた。品川なので迂回はしやすいから、まだましだったけど(苦笑)


HBase勉強会(第二回)の感想

2011-06-17 04:29:27 | PG(NoSQL)

第2回HBase勉強会に参加しましたので、例によって感想など書いてみます。
Togetter - 第2回HBase勉強会 #hbaseworkshop

ちなみに第1回は不参加…実務でHBase使ってるわけじゃないので資格外かなーと思ったのと、18:30からだと微妙に間に合わない。今回は19:00からだった(ことにぎりぎり気付いた)のでラッキー♪
しかし京浜東北線が人身事故で山手線まで止まってて間に合わないかと思った^^; 品川駅なんかすごい混雑で、ホームから人が落ちて新たな事故にならないのが不思議なくらい(汗) 電車内が混んでるのは何度も経験してるけど、駅で人混みに押されるなんて初めて(苦笑)


今回はtatsuya6502さんが体調不良でお休みということで、残念。お大事に。

代わりにueshinさんの発表からスタート。→資料
いきなり自宅サーバーはインパクトあるなぁ(笑) しかしスペックとか構成とか、こういう実例は嬉しい。
特にZooKeeperをDataNode等とは別にした方が良いとか。同居させると負荷が高いとき(MapReduce実行時とか^^;)に反応が遅くなって落ちちゃうらしい。ZooKeeper1台構成もすぐ落ちちゃうし2台は元々非推奨なので、3台で。
3台構成なら、1台が落ちて2台になっても稼動する。
ZooKeeperがROOTテーブルの位置を保持しているので、ZooKeeperが機能しなくなったらZooKeeperへアクセスしようとリトライし続けるのではないかとのこと。

HBaseの仕組みは、物理的にはカラムファミリー毎に保存される。
リージョンはROWキー(の範囲)によって分割される。一定サイズ(デフォルトは128MB)を超えると自動的に分割される。リージョン分割中はそのリージョンへのアクセスは一時停止となる。
ここで、カラムファミリー内にデータをいっぱい入れたり、カラムファミリーそのものがいっぱい作られたらどうなるの?という質問が。たぶんコンパクションが無限に起きてしまうんじゃないかとのこと。要するにカラムファミリーはたくさん作るものじゃない、と。まぁカラムファミリーを追加するにはテーブルを使用不可にしないといけないしねぇ。
(ちなみに以前上限を聞いたと思ったが、行数10億とカラム数100万で、カラムファミリーは載ってなかった^^;)

あと、ACID特性に関連して、ROW内はatomicだが、複数ROWにまたがるatomicは無理。複数をやりたかったら、分散トランザクションを作るか、ZooKeeperでロックするか。いずれにしてもあまりやらない方が良さげ^^;


続いてokachimachiorzさんから、アーキテクチャ要件について。

MapReduce実行時とかコンパクション(リバランス)時とか、HBaseはまだ不安定に感じる、HBaseはある程度の規模を想定しているのだろうとのこと。
(そういえばHBaseとCassandraの討論会でもHBaseは5台以上欲しいという話だった)

そして、HDFSとHBaseのデータロケーションを分けるべきかもしれないという衝撃発言!
HDFSと分けるというのは、別クラスターを作る(MapReduce実行用のクラスターとHBase用クラスターを分ける)という意味だろうか。
HBaseに書き込んで出来たファイルに対して直接MapReduce出来るのが他のKVSとは違う利点だと思うので、別クラスターにデータコピーしてMapReduceするとしたら、あまり意味が無いような^^;
(実験環境はどうしても低スペック(サーバー数も少ない)になるだろうから、そこで「使えない」と誤解されてしまったら寂しいっすねぇ)

それはそれとして、okachimachiさんが考える適用業務について。
(このうちの一分野も分からないとなると、SIerとしては失格だよなぁ>< 自分は全然ダメだ)

●マスター管理
マスターの同期処理が必要。やはりフロントはRDBということで、RDBMS→HBase→HDFS

●受発注
伝票検索:特定キーと日付による検索。一括検索はHDFSでよいが、微妙なサイズ・タイミング(障害が起きた場合に一部だけ実行したい(サブバッチ)とか)はHBase。

●在庫管理
教科書はACIDで書かれているが、普通はACIDではない。フラグを立てて非同期で実行する。
指定された在庫だけ処理したい→HBase(HBaseをフロントに置く前提)
読み込みはしたい→更新時にロックしたい(更新は3時間毎で参照はリアルタイムとか)
→でもフラグを立てるだけならロック不要、すなわちHBaseでなくてもよいのでは?というツッコミが^^;
基幹バッチ処理としては一部を別の場所にコピーしてそれを処理するというのは王道だが、HBaseとHadoopのロケーションを分けてコピーすると負荷がかかるし…という感じらしい。

●原価計算
確定処理とシミュレーション(同期は不要)→結果を保存して参照するのにHBase

●物流管理
リレーショナルデータ(在庫マスターや顧客マスター)との結合あり。
Planing(計画)系とExcecution(実行)系
マテハン(マテリアルハンドリング・組込み系)への受け渡しあり。

●製造管理
計画のプランニングはHadoopで実施できる。分散MRPとしてHBaseでやれたらすごいらしい。

●生産管理
歴史的推移:昔は実行系のみ→実行系と計画系(SCM)→結合していく→ERP(ロックイン、メンテ不能)
・スケールアウトしない
・非同期処理の高速化→HBaseが使えそう。
・素結合による簡素化→レイテンシーは上がる。(人は、3秒かかると死んだと判断する)

●原子力発電管理(ネタ)
シミュレーション、センサーデータ収集分析、リアルタイム、組み込みの直接制御
リアルタイムというのは、例えば薬品を一定量入れたいとき、センサーで監視しつつ、予定の場所まで来たらすぐ止める必要がある。ミリ秒以下。
→メジャーコンパクションが起きたらアウト! したがって、HBaseでは原発管理できないw(原発を想定したNoSQLがあったら面白い…って^^;)

●販売管理
集配信→HBase(分散書込)→HDFS→HBase(分散書出)→RDB
想定件数は、多いときで1億件。HDFSでの非同期処理は15分間隔。
集信でバッファリングが要るかもしれない。
ツッコミ1:書き出しのHBaseは不要で、直接RDBへ入れれば?
ツッコミ2:データ追記型なら、書き込みのHBaseは不要で、ファイル(HDFS)でよいのでは?(ログ収集ならファイルを直接HDFSに入れる。それと同じイメージでいけるのでは?)
書き出しに関しては、同じROW(の別カラムファミリー)にデータを追加する形なら、HBaseが有効かもという考察。
HBaseは分断すると落ちる(第10回Cassandra勉強会参照)ので、Cassandraの方がいいかも。ただしデータの整合性は弱い?

※下手に継続するよりも、落ちるなら落ちた方が良い
※システムが落ちたらまず言う事は、「まず落ち着け」「落ち着いて説明しろ」w


続いてashigeruさんから、(今度は業務ではなく仕組み寄りで)HBaseの用途について。

HBaseの特徴は、
・細かいデータの多数書き込みに強い
・キー順にソート済(「隣のレコード」という概念が存在する)
・バーストリードは一応早い
・テーブルとROWが同じなら、リージョンも同じ
・カラムファミリーの追加は停止しないと出来ない
・暇なときにリバランス(リージョン分割)する⇔忙しいときはリージョン分割しない

したがって
・OLTP+インクリメンタル
・オンライン性能をあまり劣化させずにバックグラウンド処理
・バックグラウンド処理同士の干渉が少ない
・分散memtableは、バッチ処理ではあまり恩恵を得られない

これを元に、やりたい事は「締め処理を速くしたい」
締め処理の特徴
・BigDataまではいかない
・小さなマスターが多い
・大きなマスターもある
・トランザクションとの全件結合なんてことも(汗)

小さなマスターだと、そこにアクセスすると負荷が集中する可能性がある。
(そもそもHadoopではSequenceFileとかにして配布する機能があったと思うけど、大きさ次第なのかな?)

MapReduceのReduceサイドJOINだと、マスターが大きい場合は、一部しか使われないと無駄(同期はされる(ReduceタスクはMapタスクが終わるのを待つ)ので、遅くなる)。
↓逆の発想
マスターをHBaseで分散しておいて、(キーで分散しているから)その場所にトランザクションデータを配置して処理

ただし、出力先も同じリージョンになるとは限らない(キーが異なるから)

ハッシュをROWキーにして、同じ場所を強制する?(←さすがに無理があると思う(苦笑))


最後に、frsyukiさん。

ログ収集ツール(まだ未公開)の紹介。(Facebookのscribeを参考にしたそう)
先ほどの販売管理の集配信に似た仕組みということかな?

ログ→fluent→HDFS
fluent(フルーエント)でバッファリングおよびスプリット(MessagePack File)し、定期的にHDFSに保存。たまにマージジョブを実行する。
バッファリングの際にコンバイナーのような演算も出来る。
キューのような使い方も出来るが、入力は1件ずつだけど出力はチャンク単位という制限がある。

もうひとつ、MessagePack+Hadoop。
MessagePackのIDLで構造を定義し、MapReduceやHiveで指定できる。
MessagePackInputFormat・MessagePackWritableにはちょっと燃えた(笑) いいなぁ、これ!

それと、Columnar File。カラム指向ファイルということかな。
[id:1, a:11, b:202][id:2, a:13, b:204]というデータがあったら、id:[1,2],a[11,13],b[202,204]という形で保存することによって、キー名を毎回書かなくても済む分、データ量が減らせる。
また、特定キーのデータをスキップすることも簡単になる。
さすがMessagePackを作っているだけあって、そういう部分は詳しそう。

最後に非構造化データに関する質問があって、クレンジングについて話題が。クレンジングには2種類あって、
・データフォーマットが壊れている→実際には少ない。バリデーションで弾く
・セマンティクス(意味)がおかしい→必要がデータが入っていないとか値が変とか
現実には前者は少なく、後者の方が多いとのこと。


ふう、今回も盛りだくさんでした。

HBaseでどうテーブル(カラム)を持たせればいいかというのは思考実験で考えていたつもりだけど、実際の特性は知らないので、やっぱりそういう所も気にしないと駄目ですね。

ありがとうございました。


Scalaのへんなとこ?

2011-06-12 04:10:20 | PG(Scala)

kaminamiさんの「ここがヘンだよScala言語」がなかなか興味深いネタを扱っていたので、自分も(Scala勉強中なので詳しくはないけれど)ちょっと意見を書きたくなったw


if式の返り値

自分はScala2.8から勉強を始めたので それより前の事は知らないけれど、以前のバージョンでは「if(true) "a"」がUnitのインスタンスである()を返していたらしい。そりゃ僕も変だと思う^^;(個々人の直感から外れた動作は「変」と表現されてしまうのですな)

「if(false) "a"」の戻り値は()になるので、このif式の戻り値の型は、"a"(String)と()(Unit)の共通の型となる。Scala2.8ではAnyになるが、2.7ではUnitになる仕様だったのだろう。
(あるいは、elseが無いということは式というより文という使い方なので、whileとかに倣ってUnitにしていた?)

そういえば、"a"とか123がUnitに変換される仕組みもよく分からないなー。
「def f():Int = "a"」はコンパイルエラーになるが、「def f():Unit = "a"」は通る。StringからUnitへの暗黙変換がどこかにあるのか?


除外インポート

import java.sql.{ _, Array => _}」ってやつ。Arrayだけ除外できる。
Scalaは何か記号を使う箇所では、「_」を駆使するよね(笑)
じゃあここではどんな記号を使うべきかと問われたら、意外と悩むんじゃないかと思うけど。
そして、構文ごとに使う記号がまちまちになって、複雑とか言われちゃうんだろうなぁ。


typeと別名インポート

確かに、typeだけあれば別名インポート要らないかも?
と思ったけど、「type MMap = scala.collection.mutable.Map」って書けないんだよねぇ。typeでは型パラメーターを指定しないといけないから。
あ、「type MMap[A,B] = scala.collection.mutable.Map[A,B]」ってすればいいだけか(汗)
でもこれだと、Mapのコンパニオンオブジェクトが呼べない…。(「MMap[String,String]()」とか出来ない)


forのネスト

for(i <- 0 to 10; j <-0 to 10; k <- 0 to 10)」がどういうループか?
セミコロン区切りで3つに分かれてるから、C言語みたいに各ブロックとかで特殊な事をしてるのかもしれないし、でもi・j・kって書かれてるから三重ループのような気もするし?
うーん、他の言語で見かけない構文は、その言語の構文を勉強しないと分からないわなぁ。


for内包表記中のifとif式

forの括弧内で「if」を見かけたときにはビックリしたね(笑) 本体部分でifを書けばいいじゃんって思ったし。
「for(i <- 0 to 10 if i % 2 == 0) hoge」→
「for(i <- 0 to 10){ if(i % 2 == 0) hoge}」

for式がforeachとかに変換されると知って、ifがfilterメソッドになるんだったら、for式の構文上も「filter」というキーワードにすれば…とか思ったけど、英語圏の人にとってはifの方が分かりやすいのかもなーとも思う。


forとforEach

自分はJavaは知ってるけど関数型の考え方はさっぱりなので、forの構文無しでいきなりforeach使えって言われたら、敷居は高かっただろうなー。


タプルのインデックス

配列とかリストとかの先頭要素の添字は0なのに、Tupleは1から始まる。「t._1」みたいな。
これは自分も疑問に思った。(JDBCのResultSetも1から始まるのは気に入らない。BASICじゃないんだからさーw)

ただ、関数型言語ではタプルは1から始まるのが一般的らしい?
「ある言語内で(0オリジンに)統一する」のと、「(他言語も含めた)一般的な法則に従う」のと、どちらを採るかという話。


overrideの有無

自分は、抽象メソッドを実装するときにもoverrideを付けるべきだと思う。
「class B extends A { def name = ~ }」というソースを見たとき、nameが親クラスで宣言されているのか、自クラス専用なのか、そのソースだけでは判断できない。
オーバーライドしているのであれば、勝手にメソッド名を変えることは出来ない。
まぁ、抽象メソッドだったとすれば、変えたらコンパイルエラーになるので分かるんだけど…。IDEのリファクタリング機能がまだ期待できないので、簡単に変えられない。JavaでEclipseなら、けっこう気軽に名称変更するんだけど。


インスタンス生成時のnewの有無

最初分からなかったなぁ。newの要るケースと要らないケース。Java脳では、newしない事はありえないしなぁ。
分かってみれば、パターンは単純だけど。
(そういやC++もnewを使わないでインスタンス生成できるなぁ。あれも意味合いがけっこう違うが)


コンストラクター

classのブロック内に記述したコードが直接コンストラクターになるっていうのは、すごいアイデアだと思ったね!(コロンブスの卵的な意味で)
いわば、Javaのインスタンス初期化子の考え方を拡張したもの。コンストラクターが1つしか無いなら、def this()みたいなコードが不要で、すっきりする。(コンストラクター内のローカル変数が定義できないけど^^;)
一方、「どのコンストラクターも全てthisという名前にする」となっていたら、統一が取れているとも言える。


プレースホルダー

関数で各引数を1回しか使わない場合はプレースホルダー「_」が使える。
「_ < _」は「(a,b) => a < b」と同じだけど、引数の使用順序を変えたり2回以上使いたい場合は、どんな記号であれ一種類では表現できないよね。
「_1 < _2」みたいな表記が出来れば順序を変える事も2回以上使うことも可能だけど、数値を入れている時点で、引数の順序に依存してる(苦笑)
つまり、変数名を明示的に付けない限り、順序依存になるのはやむなし?(なるべく順序に依存すべきではない、という考え方には賛成なんだけど)

(しかしメソッド呼び出しなんかも、引数名を指定する形式にしない限り、順序に依存してるな…)


特殊なメソッドの書き方

一部のメソッド名に特別な意味があるのは自分も本当はあまり好きじゃない。それこそ用途毎にアノテーションとか付けるべきじゃないかと思う。
(JavaでRuntimeExceptionの派生クラスはthrowsが不要とか、特定のクラス名が言語仕様に入っているのは変だと思う)

と思ったけど、例えばapplyメソッドの代わりに@applyみたいなアノテーションがあったとして、「@apply apply1()」と「@apply apply2()」の2つが定義されていたら、呼び出し側でメソッド名を省略すると、どちらが呼ばれるべきか分からなくなるな(苦笑)
しかも親クラスで@apply付きのメソッドを定義してたりしたら…。


似ているキーワード

NoneNothingNullnullNilって、字面は似てるのかもしれないけど、全然別物で使い方も違うし、非常に分かりやすい。

それならListの::とかfuture(scala.concurrent.ops.futureとscala.actors.Futures.future)の違いの方がよっぽど分かりづらい。(てかfutureの違いは自分には分からない(汗))


パラメーター境界で右結合

Scalaのメソッドでは、メソッド名の末尾がコロンだと右結合になる。
これを「Scalaのメソッドでは」でなく「Scalaでは」という覚え方をしてしまうと、パラメーター境界の「<:」も気になるかも^^;

ちなみにJavaの言語仕様書の型の説明で初めて「<:」とかを見た気がするが、そのときはさっぱり理解できんかった(爆)


コンパニオンの参照

個々のクラス(インスタンス)から、コンパニオンオブジェクトのインスタンスを取得すること。
これは欲しいかも。
ついでに、Enumerationで、列挙子から列挙クラス(オブジェクト)を取得する方法も欲しいかも。(Enumeration自体、評判悪いようだけど…)


unsignedが無い

Javaにも無いからなぁ。Scalaには、JavaVMから独立してScalaVM作って欲しいな(笑)
(そのときにunsignedが入るかどうかは知らないけど、型消去は無くなるでしょう)


大抵の事は、何かしらそうなっている理由があるわけで。

ただ、その説明を聞いたときに「なるほど!」と思う人と「(理解できん)複雑だ」と思う人と、「分かったけどやっぱり複雑だ」という事柄があるんだろうなぁ。
あと、「説明を聞かないと理解できないようなものは「複雑だ」」とか「説明をじっくり見てる暇が無くて、ぱっと見た感じ複雑」という人もいるかも。

差し当たり、モナドって複雑だよね(爆)