Slide 1

Slide 1 text

InfluxDB & LevelDB Inside-out

Slide 2

Slide 2 text

Metrics を保存するための時系列 DB の話をします InfluxDB Part1. LevelDB Part2.

Slide 3

Slide 3 text

  InfluxDB は絶賛開発中です (NOT product ready)  情報が古くなったらスライドを捨ててください

Slide 4

Slide 4 text

InfluxDB Part1.

Slide 5

Slide 5 text

は 保存のために 作られた時系列 • Y Combinator から出資を受けた スタートアップ Errplane が開発 • モニタリング・アラート SaaS を立ち上げるため のバックエンドのデータベースとして開発 • ビジョンと機能が特徴的で注目を集めている

Slide 6

Slide 6 text

(from https://angel.co/errplane)

Slide 7

Slide 7 text

特徴的な点を順に紹介する • HTTP API • Powerful query language • Built in explorer

Slide 8

Slide 8 text

から 操作 クエリ発行 ブラウザの Javascript インタプリタから 直接 DB 操作やクエリ発行が可能 # Create a database $ curl -sL -w "%{http_code}¥n" ¥ -X POST ¥ 'http://db:8086/db?u=root&p=root' ¥ -d '{"name": "test1"}‘ 201 Response code が 2xx なら成功

Slide 9

Slide 9 text

# Get list of databases $ curl -s ¥ 'http://db:8086/dbs?u=root&p=root' ¥ | python -mjson.tool [ { "name": "test1" }, { "name": "dummy" } ] * NOTE: 2013-11-20 Undocumented feature Response body が JSON で得られる 結果は で得る

Slide 10

Slide 10 text

# Add database user $ curl -sL -w "%{http_code}¥n" ¥ –X POST ¥ ‘http://db:8086/db/test1/users?u=root&p=root’ ¥ -d ‘{“username”: “smly”, “password”: “yeah”}’ 200 # Get list of database users $ curl –s ¥ ‘http://db:8086/db/test1/users?u=root&p=root’ ¥ | python -mjson.tool [ { “username”: “smly” }, ]

Slide 11

Slide 11 text

# Write points $ curl -sL -w "%{http_code}¥n" ¥ –X POST ¥ ‘http://db:8086/db/test1/series?u=smly&p=yeah’ ¥ -d ‘[{ “name”: “events”, “columns”: [“uid”, “action”, “item”, “price”], “points”: [ [“194016”, “BUY”, “watch”, 19999], [“194016”, “CLICK”, “watch”, 19999] ] },{ “name”: “error”, “columns”: [“uid”, “action”, “errorName”], “points”: [ [“194016”, “BUY”, “NullPointException”] ] }]’ 200 Database username/password Series name (like MySQL’s table) Series name

Slide 12

Slide 12 text

# Write points $ curl -sL -w "%{http_code}¥n" –X POST ¥ ‘http://db:8086/db/test1/series?u=smly&p=yeah’ ¥ -d ‘[{ “name”: “events”, “columns”: [“uid”, “action”, “item”, “price”], “points”: [ [“5485”, “CLICK”, “watch”, 19999], ] }, { “name”: “events”, “columns”: [“uid”, “action”], “points”: [ [“5485”, “LEAVE”], ] }, }]’ 200 Schema-less !!

Slide 13

Slide 13 text

point は “time” column が自動で追加される. これは指定することもできる. # Write points $ curl -sL -w "%{http_code}¥n" –X POST ¥ ‘http://db:8086/db/test1/series?u=smly&p=yeah& time_precision=s’ -d ‘[ { “name”: “events”, “columns”: [“time”, “uid”, “action”], “points”: [ [1385390220, “5485”, “LEAVE”], ] }, }]’ 200 Need to specify the precision of the value Seconds (s) , milliseconds (m) or microseconds (u)

Slide 14

Slide 14 text

Gist: https://gist.github.com/smly/7711109 (requires influxdb-python) • Generate & Write 1 point for each 10 seconds. • Totol points: 6 * 60 * 24 * 30 (30 days) $ git clone gist.github.com/7711109 $ cd 7711109 $ python write.py ...........................done

Slide 15

Slide 15 text

で 時系列データを扱うことができる Support many useful aggregation/summarize functions!! $ python > from InfluxDB import InfluxDBClient > c = InfluxDBClient( ‘root’, ‘root’, ‘smly’, ‘secret’, ‘testdb’) # Get points in last hour > c.query(“”” SELECT * FROM markov “””)

Slide 16

Slide 16 text

# Get points in recent 10 minutes > c.query(“”” SELECT * FROM markov WHERE time > now() – 10m “””) [{ “name”: “foo”, “columns”: [ “time”, “sequence_number”, “value” ], “points”: [ [1385399228, 369291, 100], [1385399218, 369292, 100.677049], ... ] }] now() と時間リテラルで 直近の 10 分のデータを指定 The `time` and `sequence_number` Columns are special built-in columns.

Slide 17

Slide 17 text

# Get mean value grouped by 5 minutes > c.query(“”” SELECT MEAN(value) FROM markov GROUP BY time(5m) WHERE time > now() – 1h “””) [{ “name”: “foo”, “columns”: [ “time”, “sequence_number”, “mean” ], “points”: [ [1385399100, 1, 103.01160280567923], [1385398800, 1, 102.77647754870585], ... ] }, {…}] 5 min ごとの time で GROUP BY して value の平均を計算 他, COUNT, DISTINCT など SQL にある様々な関数を 実装している

Slide 18

Slide 18 text

正規表現や複数の の選択ができる Schema-less なので SQL にできない複数 series の選択も可能 (time の指定がない場合は last hour から選択する) SELECT * FROM events, errors; SELECT * FROM /stats¥..*/i; SELECT * FROM events WHERE state == ‘NY’ SELECT * FROM log_lines WHERE line =~ /error/i; SELECT * FROM response_times WHERE value > 500; SELECT * FROM nagios_checks WHERE status != 0;

Slide 19

Slide 19 text

からクエリ発行&可視化 (from http://obfuscurity.com/2013/11/My-Impressions-of-InfluxDB)

Slide 20

Slide 20 text

便利だけど確認していない • InfluxDB v0.3.0 • Chromium 30.0.1599.114 • Mozilla Firefox 25.0.1

Slide 21

Slide 21 text

Cron を書いて定期的に削除しなくてもスケジュー ル実行できるようになりそう Dashboard 上から発行されるクエリは read 権限だけを持つ ユーザーによるといった使い方ができるようになりそう

Slide 22

Slide 22 text

による分散協調 2013-11-30 時点の #PR 20 を読んだ感想。 まだ master には merge されていないので変更あるか も • “Database, series, timestamp” で hash 化して得られた RingLocation 値ごとにデータを分散させる – Timestamp で区切ると効率悪くならないか? • 同一の RingLocation 値を持つサーバーで Raft による 協調&Replication が行われる – ref: http://raftconsensus.github.io/ – Ref: https://github.com/influxdb/influxdb/pull/20

Slide 23

Slide 23 text

No content

Slide 24

Slide 24 text

Query Engine Coordinator (Raft) HTTP API Storage Engine Storage Engine … Web UI Port: 8086 Port: 8083

Slide 25

Slide 25 text

Query Engine Coordinator (Raft) HTTP API Storage Engine Storage Engine … Web UI Port: 8086 Port: 8083

Slide 26

Slide 26 text

A Sinatra style pattern muxer for Go's net/http (https://github.com/bmizerany/pat.go)

Slide 27

Slide 27 text

pat.go の Get, Post, Del を wrap したメソッド registerEndpoint を HTTPServer に実装している

Slide 28

Slide 28 text

Query Engine Coordinator (Raft) HTTP API Storage Engine Storage Engine … Web UI Port: 8086 Port: 8083

Slide 29

Slide 29 text

Flex (Fast lexical analyzer generator) + YACC (Compiler-Compiler) + Bison (CFG perser generator; LALR) 1. DURATION, 2. REGEX_STRING, 3. INSENSITIVE_REGEX_STRING の字句

Slide 30

Slide 30 text

struct を定義して初期化時に hash に登録する (面白いネタがないので省略)

Slide 31

Slide 31 text

Query Engine Coordinator (Raft) HTTP API Storage Engine Storage Engine … Web UI Port: 8086 Port: 8083

Slide 32

Slide 32 text

LevelDB を wrapper した Levigo を使用 (*1) 性能に影響を与える LRUCache size や Bloom filter の bits size は固定値(暫定的な感じ) *1: 開発者によって “may change” であると発表されている。 “Introduction to InfluxDB by Paul Dix” http://g33ktalk.com/introduction-to-influxdb

Slide 33

Slide 33 text

LevelDB のデータストアは 1 つだけ作成. 2種類の Key:value のペアが格納される(厳密ではない) Column / Column ID を管理するための key/value pair Key: “[PREFIX]~[DB]~[SERIES]~[COLUMN]”, Value: ColumnID Point / Data を管理するための key/value pair Key: [TIMESTAMP][SEQ_NUM][ColumnID], Value: Data

Slide 34

Slide 34 text

の記録例 [{ “name”: “events”, “columns”: [“uid”, “action”, “item”, “price”], “points”: [ [“5485”, “CLICK”, “watch”, 19999], ] }, { “name”: “events”, “columns”: [“uid”, “action”], “points”: [ [“5485”, “LEAVE”], ] }, }] [PREFIX]~test1~events~uid: 1 [PREFIX]~test1~events~action: 2 [PREFIX]~test1~events~item: 3 [PREFIX]~test1~events~price: 4 1384951400_0000001_1: “5485” 1384951400_0000001_2: “CLICK” 1384951400_0000001_3: “watch” 1384951400_0000001_4: “19999” 1384951400_0000001_5: “5485” 1384951400_0000001_6: “LEAVE” 以下のように記録される(厳密ではない) Column/ColumnID の key:value

Slide 35

Slide 35 text

の記録例 [{ “name”: “events”, “columns”: [“uid”, “action”, “item”, “price”], “points”: [ [“5485”, “CLICK”, “watch”, 19999], ] }, { “name”: “events”, “columns”: [“uid”, “action”], “points”: [ [“5485”, “LEAVE”], ] }, }] 以下のように記録される(厳密ではない) Point/Data の key:value [PREFIX]~test1~events~uid: 1 [PREFIX]~test1~events~action: 2 [PREFIX]~test1~events~item: 3 [PREFIX]~test1~events~price: 4 1384951400_0000001_1: “5485” 1384951400_0000001_2: “CLICK” 1384951400_0000001_3: “watch” 1384951400_0000001_4: “19999” 1384951400_0000001_5: “5485” 1384951400_0000001_6: “LEAVE”

Slide 36

Slide 36 text

Column/ColumnID に対する key:value DB, Series, Column に対して固有の ID を GET/PUT [db]~[series]~[column] で key 作成

Slide 37

Slide 37 text

Point/Data に対する key:value Point は timestamp, seqNum, Columnid を key とする 8 bytes の Buffer (x2) を作って連結して key 作成

Slide 38

Slide 38 text

• HTTP API と Web UI でブラウザからすぐ使える • SQL-like query と便利拡張が使える • statsd, collectd などと連携すればHTML, CSS, JavaScript だけで Dashboard を開発できそう (誰か Kibana っぽいもの作ってください) (ref: https://github.com/obfuscurity/tasseo)

Slide 39

Slide 39 text

LevelDB Part2.

Slide 40

Slide 40 text

の特徴 LSM-tree を採用した write-performance optimized な database library • 高速 • Relational data model ではなく key-value storage • Key と Value は任意のバイト列 • Key はソートされて保存される • Forward/Backward iteration をサポート Paper: ” The Log-Structured Merge Tree (LSM-Tree)” http://www.cs.umb.edu/~poneil/lsmtree.pdf

Slide 41

Slide 41 text

データ構造 B-tree を Cascade することで insert が頻出する場面においても 低いコストでリアルタイムにインデックスを保持できる。 小さい木から、指数関数的に大きな木まで、深さの上限が定 められている木の集まりで構成される。 C0 C1 C2 C3 Memory Disk | C3 |

Slide 42

Slide 42 text

C0, C1, C2 … と複数の木から探さなくてはならない ケースがあるため B-tree より不利 C0 C1 C2 C3 Memory Disk

Slide 43

Slide 43 text

Range query も同様。 C0, C1, C2 … と複数の木から探さなくてはならない ケースがあるため B-tree より不利 C0 C1 C2 C3 Memory Disk Iter Iter Iter Iter

Slide 44

Slide 44 text

C0 C1 C2 C3 Memory Disk INSERT 上限まで木が成長したら次のレベルの木に Merge.

Slide 45

Slide 45 text

上限まで木が成長したら次のレベルの木に Merge. C0 C1 C2 C3 Memory Disk INSERT (reach limited size)

Slide 46

Slide 46 text

上限まで木が成長したら次のレベルの木に Merge. C0 C1 C2 C3 Memory Disk Merge

Slide 47

Slide 47 text

上限まで木が成長したら次のレベルの木に Merge. 深くない木を使うことで balancing のコストを軽減 C0 C1 C2 C3 Memory Disk INSERT

Slide 48

Slide 48 text

Update は point search したときに hit したノー ドを更新 C0 C1 C2 C3 Memory Disk

Slide 49

Slide 49 text

Delete はリクエスト時は削除フラグを立てるだけ. Merge 時に実際にノードを削除する. C0 C1 C2 C3 Memory Disk DELETE 削除フラグ

Slide 50

Slide 50 text

Delete はリクエスト時は削除フラグを立てるだけ. 実際にノードを削除するのは Merge 時 (Balancing のコストを軽減) C0 C1 C2 C3 Memory Disk Merge 削除

Slide 51

Slide 51 text

の に LevelDB は C0 に LockFreeSkipList (aka ConcurrentSkipList; CSL) を採用している C0 C1 C2 C3 Memory Disk ?

Slide 52

Slide 52 text

詳細は以下の書籍 Chapter 14 を参照すべき linked-list をたどるための 近道がレベルごとに用意されている構造

Slide 53

Slide 53 text

なぜ の に か? 何故これ (SkipList) をLSM-Treeで使おうと 思ったかが未だちょっと理解できていない のですが、どこかに解説はないでしょうか。 “LevelDB の雑感 – くまメモ” http://d.hatena.ne.jp/kumagi/20111201/1322735619 “ Contributor が LSM-tree の C0 (memtable) に Hash を使わない 理由として, (1) Sorting と (2) Concurrency を挙げている http://t1825.db-leveldb.dbtalk.us/a-question-about-leveldb-s- skip-list-t1825.html kwsk

Slide 54

Slide 54 text

の は されていないので却下 Sorting されていないと Iterator の support が困難。 従って Hash は却下。 C0 Memory Iter

Slide 55

Slide 55 text

の 論文 は を想定しているが • C0 の木として B-tree like ではない AVL-tree などを想定している • 常にメモリ上にある木であるため, Disk page size nodes である必要がないから

Slide 56

Slide 56 text

の の性能が良い 今は Multi-core/Multi-thread 当たり前の時代 LockFreeSkipList が脚光を浴び始める。 AVL-tree とかは深さを対数に維持するため Rebalancing を必要とする。 並列プログラムにおいて Rebalancing は ボトルネックや競合状態を発生させる可能性がある。 “ The Art of Multiprocessor Programming, “14. Skiplists and Balanced Search”

Slide 57

Slide 57 text

“ の の性能が良い Good high contention performance, comparable single-thread performance. In the multithreaded case (12 workers), CSL tested 10x faster than a RWSpinLocked std::set for an averaged sized list (1K - 1M nodes). https://github.com/facebook/folly/blob/master/folly/ConcurrentSkipList.h folly 曰く、”マルチスレッドの場合 10 倍高速”

Slide 58

Slide 58 text

の 結論 Itetator support ほしいのでソートされた データ構造がほしい。 Concurrency を考えるとパフォーマンス面で LockFreeSkipList は妥当な選択であると思われる。

Slide 59

Slide 59 text

におけるチューニング • 古い話。LevelDB 1.2 は 2011-05-16 リリース • On-disk size を大きくして file open を減らす • Bloom Filter を導入して Throughput 底上げ Ref: http://basho.com/leveldb-in-riak-1-2/

Slide 60

Slide 60 text

による の効率化 探索を Bloom Filter による O(1) の操作で 省略することができる(枝刈り) C0 C1 C2 C3 Memory Disk O(1) で探索の 必要なしと判断 O(1) で探索の 必要なしと判断

Slide 61

Slide 61 text

による効率化 無駄足は許すが 取り零しは無い Bloom Filter は LSM-tree の枝刈りに都合が良い Bloom Filter の答え Key がある Key がない 本 当 の 答 え Key が あ る True Positive (正解) False Netagive = 0 (取り零しはない) Key が な い False Positive != 0 (無駄足は許容する) True Negative (正解)

Slide 62

Slide 62 text

• InfluxDB はダッシュボード開発が容易. ブラウザから SQL-like にデータ取得できる 時系列 DB . • Metrics 保存時には高頻度の Insert が予想 される.LSM-tree ベースの LevelDB を 使っているのは妥当そう.