kuduを調べてみた #dogenzakalt
TRANSCRIPT
道玄坂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