Kuduを調べてみた #dogenzakalt

Preview:

Citation preview

道玄坂LT祭り

Kuduを調べてみた株式会社サイバーエージェント

技術本部 秋葉原ラボ鈴木俊裕

自己紹介

● 鈴木俊裕(すずき としひろ)● ソフトウェアエンジニア

● サイバーエージェント 技術本部 秋葉原ラボ○ Hadoopを用いたログ解析基盤○ HBaseを用いた基盤システム

● 最近の興味:Go, NewSQL● Twitter @brfrn169

● 著書「HBase徹底入門」

今日話すこと

● Kuduを調べてみた

○ Kuduについて

○ デモ(時間があれば)

Kuduについて

Kuduとは

● Clouderaが開発したHadoopのエコシステム

● カラムナ(列指向)ストレージエンジン

● オープンソース(Apache インキュベータプロジェクト)

Kuduの位置づけ

● HDFS + Parquet or ORC○ スキャン ◎○ ランダムアクセス ☓

● HBase, Cassandra○ スキャン ☓ or △○ ランダムアクセス ◎

● Kudu○ スキャン ◯○ ランダムアクセス ◯

Kuduを使うと何が嬉しいのか

● スキャンもランダムアクセスも必要なユースケース

○ Kuduがなかった時には複雑なアーキテクチャになりがち

Kuduを使うと何が嬉しいのか

● スキャンもランダムアクセスも必要なユースケース

○ Kuduを使うとシンプルになる

データモデル

● RDBに似ている

○ Tableを事前定義する必要がある(CREATE TABLE)■ 主キーを必ず定義する

■ 有限個のColumnを定義

● 動的にColumnを追加できない

○ Columnは型を持つ(e.g. INT32, STRING)○ 今のところ、セカンダリインデックスやユニークキー制約な

どは実装されてない

データモデル

● HBase等のように値をbytesにしなかった理由

○ 型に特化したエンコードや圧縮が可能だから

● サポートしているエンコード

○ Bitshffule, Run Length, Dictionary, Prefix● サポートしている圧縮

○ LZ4, snappy, zlib

API

● Java, C++, Python(実験段階)

● 書き込みは Insert, Update, Delete○ 主キーを指定しなければならない

○ (HBase等のように)書き込み時にタイムスタンプを指定で

きない

○ 複数RowのトランザクショナルAPIはない

■ 単一Row内の更新はアトミック

API

● 読み込みは Scan のみ

○ Filterの指定はできる

■ Colmunの値と定数の比較(e.g. col = “aaa”)■ 主キーのレンジ(e.g. key >= 100 AND key <= 200)

○ プロジェクション(射影)を指定できる(e.g. SELECT key, col where ...)■ KuduはカラムナストレージエンジンなのでColumnを絞

るとパフォーマンスがよくなる

○ タイムスタンプを指定して point-in-time クエリが可能

インテグレーション

● Impala○ 将来的にはHive, Drill, Prestoも?

● Spark○ DataSourceとしてKuduのテーブルを指定できる

● MapReduce○ KuduTableInputFormat/KuduTableOutputFormat

アーキテクチャ

● データのパーティショニング

○ TableはTabletと呼ばれる単位に分割される

■ Rowは必ず1つのTabletに配置される

■ どのTabletに所属するかは主キーで決まる

○ パーティショニングの方式は2種類

■ key-range-based■ hash-based■ 組み合わせも可能

アーキテクチャ

● データのパーティショニング

○ key-range-based の例

CREATE TABLE customers ( id STRING, name STRING, age INT)DISTRIBUTE BY RANGE(id)SPLIT ROWS(('a'), ('b'), ('c'), .., ('y'), ('z'))TBLPROPERTIES('storage_handler' = 'com.cloudera.kudu.hive.KuduStorageHandler','kudu.table_name' = 'customers','kudu.master_addresses' = 'kudu-master1:7051','kudu.key_columns' = 'id');

アーキテクチャ

● データのパーティショニング

○ hash-based の例

CREATE TABLE customers ( id STRING, name STRING, age INT)DISTRIBUTE BY HASH(id) INTO 16 BUCKETSTBLPROPERTIES('storage_handler' = 'com.cloudera.kudu.hive.KuduStorageHandler','kudu.table_name' = 'customers','kudu.master_addresses' = 'kudu-master1:7051','kudu.key_columns' = 'id');

アーキテクチャ

● データのレプリケーション

○ Raftを採用している

Client

Tabletサーバ

Tabletサーバ Tabletサーバ

Tablet(LEADER)

Tablet(FOLLOWER)

Tablet(FOLLOWER)

WAL

WAL WAL

アーキテクチャ

● データのレプリケーション

○ Raftを採用している

Client

Tabletサーバ

Tabletサーバ Tabletサーバ

Tablet(LEADER)

Tablet(FOLLOWER)

Tablet(FOLLOWER)

WAL

WAL WAL

Write

アーキテクチャ

● データのレプリケーション

○ Raftを採用している

Client

Tabletサーバ

Tabletサーバ Tabletサーバ

Tablet(LEADER)

Tablet(FOLLOWER)

Tablet(FOLLOWER)

WAL

WAL WAL

WALに書き込む

UpdateConsensus() UpdateConsensus()

アーキテクチャ

● データのレプリケーション

○ Raftを採用している

Client

Tabletサーバ

Tabletサーバ Tabletサーバ

Tablet(LEADER)

Tablet(FOLLOWER)

Tablet(FOLLOWER)

WAL

WAL WAL

WALに書き込むWALに書き込む

アーキテクチャ

● データのレプリケーション

○ Raftを採用している

Client

Tabletサーバ

Tabletサーバ Tabletサーバ

Tablet(LEADER)

Tablet(FOLLOWER)

Tablet(FOLLOWER)

WAL

WAL WAL

Success

過半数が成功したら...

Success

アーキテクチャ

● データのレプリケーション

○ Raftを採用している

Client

Tabletサーバ

Tabletサーバ Tabletサーバ

Tablet(LEADER)

Tablet(FOLLOWER)

Tablet(FOLLOWER)

WAL

WAL WAL

Success

アーキテクチャ

● 2つのコンポーネント

○ Master■ メタデータの管理

● Tabletの場所等

■ Tabletサーバのコーディネーション

● 死活監視・フェイルオーバー

○ Tabletサーバ

■ 担当Tabletの実際のデータのやり取り

アーキテクチャ

● Master と Tabletサーバ

まとめ

● データモデルはRDBに近い

● APIはInsert, Update, Delete, Scan● TableはTabletにパーティショニングされる

● パーティション方式はkey-range-based, hash-based, それら

の組み合わせ

● レプリケーションはRaftで● Master + Tabletサーバ 構成

デモ

インストール

● 詳細は省略

● Cloudera Managerを使えば簡単

○ Kudu■ http://getkudu.io/docs/installation.html

○ Impala_Kudu■ http://getkudu.io/docs/kudu_impala_integration.html

● ただし、CPUがSSE4.2やSSSE3をサポートしていないと起動

しない

デモ

● Cloudera Manager● KuduのWebUI● Impalaで操作してみる

○ テーブルを作ってみる

○ データを入れてみる

○ クエリを投げてみる

○ 更新してみる

終わり

● ご清聴ありがとうございました!

資料は作ったけど話せなかった内容

整合性モデル

● デフォルトではExternal Consistencyの保証を提供してない

○ 2つのRowへの書き込みがあった時にクライアント間でど

ちらの更新が先に見えるかわからない

● 2つのConsistency Mode○ CLIENT_PROPEGATED○ COMMIT_WAIT

整合性モデル

● CLIENT_PROPEGATED○ AsyncKuduClient#getLastPropagatedTimestamp()○ AsyncKuduClient#setLastPropagatedTimestamp()

○ 更新した後に propagetedTimestamp を取得して、別クラ

イアントでそれを指定して取得する

整合性モデル

● COMMIT_WAIT○ KuduSession#setExternalConsistencyMode()で

COMMIT_WAITを指定

○ Spannerと同じ手法

○ 確実にcommitが終わっている時間まで待つイメージ

○ ただし、現状だと結構遅い(100-1000ms)■ NTPベースのため?

フェイルオーバ

● Raft○ TabletのレプリカのLEADERがダウンしたらリーダエレク

ションされて、新しいリーダーが選出される

○ FOLLOWERが落ちても問題ない((N-1)/2 まで)○ MasterもメタデータをTableとして管理してるので同様。

TabletのレプリカのLEADERがActive Masterとなる

Tabletストレージ

● Tabletは更にRowSetと呼ばれる単位に分割される

● RowSetは2種類ある

○ MemRowSet○ DiskRowSet

Tabletストレージ

● MemRowSet○ メモリー上にある

○ 各Tabletに1つだけある

○ Insertされたデータはまずここに入る

○ 主キーでソートされた状態で格納される

○ 定期的にディスクにフラッシュされてDiskRowSetになる

Tabletストレージ

● DiskRowSet○ ディスクに配置される

○ 各Column毎に別ファイルとしてディスクに書かれる

○ MemRowSetが主キーでソートされているので、必然的に

DiskRowSetも主キーでソートされる

○ 主キーのインデックスや、Bloom Filterも書き出される

Tabletストレージ

● INSERT時

ClientINSERT

MemRowSet

Tablet

Tabletストレージ

● INSERT時

ClientINSERT

MemRowSet

Tablet

flush

DiskRowSet1

col1 col2 col3

Tabletストレージ

● INSERT時

ClientINSERT

MemRowSet

Tablet

flush

DiskRowSet2

col1 col2 col3

DiskRowSet1

col1 col2 col3

Tabletストレージ

● UPDATE, DELETE時には、更新情報がRowSet毎にある delta store に置かれる

● delta store も2種類ある

○ DeltaMemStore■ メモリ上

■ 更新されたらまずここに更新情報を書く

○ DeltaFile■ DeltaMemStoreがフラッシュされてDeltaFileになる

Tabletストレージ

● UPDATE・DELETE時

MemRowSet

DiskRowSet2

col2 col3

DiskRowSet1

col2 col3

col1

col1

Tablet

Tabletストレージ

● UPDATE・DELETE時

MemRowSet

DiskRowSet2

col2 col3

DiskRowSet1

col2 col3

col1

col1

BloomFilterや主キーのインデックスを使って更新するRowを保持しているRowSetを探す

Tablet

Tabletストレージ

● UPDATE・DELETE時

MemRowSet

DiskRowSet2

col2 col3

DiskRowSet1

col2 col3

col1

col1DeltaMemStore

Rowが見つかったら更新情報を該当RowSetのDeltaMemStoreに入れる

Tablet

Tabletストレージ

● Scan時○ 指定されたColumn毎にScan

■ 主キーのレンジが指定されていたら、RowSetをスキャ

ンする範囲を減らせるか決める

■ RowSetを読みだしながら結果を作っていく

■ 最後にdelta storeに更新情報があるかどうか

Tabletストレージ

● Scan時

MemRowSet

DiskRowSet2

DiskRowSet1

DeltaMemStore

Tablet

col2 col3

col2 col3

col1

col1

Tabletストレージ

● Scan時

MemRowSet

DiskRowSet2

DiskRowSet1

DeltaMemStore

Tablet

col2 col3

col2 col3

col1

col1

指定されたColumnをScan

Tabletストレージ

● Scan時

MemRowSet

DiskRowSet2

DiskRowSet1

DeltaMemStore

Tablet

col2 col3

col2 col3

col1

col1

delta storeと突き合わせる

Tabletストレージ

● Kuduのスキャンとランダムアクセスとのトレードオフ

○ 更新時に既にRowが存在するかを確認する必要があるの

で遅くなる

○ Scan時にRowSet間のマージが必要ないので高速

Tabletストレージ

● Delta Compaction○ delta store が多くなってくると、TabletのScanのが遅くなっ

ていく

○ 定期的にbase dataとdelta sotreをマージする

Tabletストレージ

● Delta CompactionDiskRowSet

col2 col3col1 DeltaMemStoreDeltaFileDeltaFileDeltaFile

Base Data Base Dataからの差分

Tabletストレージ

● Delta CompactionDiskRowSet

col2 col3col1 DeltaMemStoreDeltaFileDeltaFileDeltaFile

DiskRowSet

col2 col3col1 DeltaMemStoreDeltaFile(REDO)

DeltaFile(UNDO)

新しいBase Data

マージされなかったDeltaFile

Base Dataより過去の差分(point-in-timeクエリ用)

Tabletストレージ

● RowSet Compaction○ 削除されたデータの物理削除

○ Key RangeがオーバラップしたDiskRowSetを減らす

■ Rowが含まれてるかもしれないDiskRowSetを減らす

ことができるで効率が上がる

Tabletストレージ

● RowSet Compaction

DiskRowSet1PK=alice PK=joe PK=linda PK=linda

DiskRowSet2PK=bob PK=jon PK=mary PK=zeke

DiskRowSet3PK=carl PK=julie PK=omar PK=zoe

Tabletストレージ

● RowSet Compaction

DiskRowSet1PK=alice PK=joe PK=linda PK=linda

DiskRowSet2PK=bob PK=jon PK=mary PK=zeke

DiskRowSet3PK=carl PK=julie PK=omar PK=zoe

DiskRowSet4PK=alice PK=bob PK=carl PK=joe

DiskRowSet5PK=jon PK=julie PK=linda PK=mary

DiskRowSet6PK=omar PK=zach PK=zeka PK=zoe