Upgrade to Pro — share decks privately, control downloads, hide ads and more …

InfluxDB & LevelDB Inside-out

@smly
November 30, 2013
5.8k

InfluxDB & LevelDB Inside-out

Monitoring Casual Talks in Kyoto #5
http://www.zusaar.com/event/1377006

@smly

November 30, 2013
Tweet

Transcript

  1. InfluxDB & LevelDB
    Inside-out

    View full-size slide

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

    View full-size slide


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

    View full-size slide

  4. InfluxDB Part1.

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

  8. から 操作 クエリ発行
    ブラウザの 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 なら成功

    View full-size slide

  9. # 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 で得られる
    結果は で得る

    View full-size slide

  10. # 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”
    },
    ]

    View full-size slide

  11. # 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

    View full-size slide

  12. # 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 !!

    View full-size slide

  13. 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)

    View full-size slide

  14. 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

    View full-size slide


  15. 時系列データを扱うことができる
    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
    “””)

    View full-size slide

  16. # 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.

    View full-size slide

  17. # 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 にある様々な関数を
    実装している

    View full-size slide

  18. 正規表現や複数の の選択ができる
    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;

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

  22. による分散協調
    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

    View full-size slide

  23. Query Engine
    Coordinator (Raft)
    HTTP API
    Storage Engine
    Storage Engine

    Web UI
    Port: 8086
    Port: 8083

    View full-size slide

  24. Query Engine
    Coordinator (Raft)
    HTTP API
    Storage Engine
    Storage Engine

    Web UI
    Port: 8086
    Port: 8083

    View full-size slide

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

    View full-size slide

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

    View full-size slide

  27. Query Engine
    Coordinator (Raft)
    HTTP API
    Storage Engine
    Storage Engine

    Web UI
    Port: 8086
    Port: 8083

    View full-size slide

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

    View full-size slide

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

    View full-size slide

  30. Query Engine
    Coordinator (Raft)
    HTTP API
    Storage Engine
    Storage Engine

    Web UI
    Port: 8086
    Port: 8083

    View full-size slide

  31. 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

    View full-size slide

  32. 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

    View full-size slide

  33. の記録例
    [{
    “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

    View full-size slide

  34. の記録例
    [{
    “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”

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

  38. LevelDB Part2.

    View full-size slide

  39. の特徴
    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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

  52. なぜ の に か?
    何故これ (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

    View full-size slide


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

    View full-size slide


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

    View full-size slide


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

    The Art of Multiprocessor Programming,
    “14. Skiplists and Balanced Search”

    View full-size slide



  56. の性能が良い
    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 倍高速”

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

  60. による効率化
    無駄足は許すが 取り零しは無い
    Bloom Filter は LSM-tree の枝刈りに都合が良い
    Bloom Filter の答え
    Key がある Key がない





    Key



    True Positive (正解)
    False Netagive = 0
    (取り零しはない)
    Key



    False Positive != 0
    (無駄足は許容する)
    True Negative (正解)

    View full-size slide

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

    View full-size slide