InfluxDBに配列データは入れられない(bitFlyerの板情報を記録したい)

April 03, 2020

そもそもデータベースは項目数が可変する設計はできないはず。bitFlyerの配列で入ってくる板情報を使い設計を考える。

入ってくるデータ

投資に関係ないユーザーでも参考になるようにデータに関して少し説明する。

次のようなJSONデータを時系列DBで正しく扱えるのか、という命題だ。

// message
{
  "mid_price": 33320,
  "bids": [
    {
      "price": 30000,
      "size": 0.1
    },
    {
      "price": 25570,
      "size": 3
    }
  ],
  "asks": [
    {
      "price": 36640,
      "size": 5
    },
    {
      "price": 36700,
      "size": 1.2
    }
  ]
}

公式に掲載されているサンプルを引用

配列になっている

ここで問題なのは bid と ask が配列になっていることだ。通常データーベースでこのような一対多のデータを扱う場合はテーブルを分けて扱うはずだが、これはそういうDBではない。

試しにやってみる

1つのテーブルに複数のデータが入れられないかやってみる。次のようなスキーマを定義する。

★Node.jsのためJavaScriptの構文で進める。

    const influx = new Influx.InfluxDB({
        host: 'localhost',
        database: 'bitFlyer_db',
        schema: [
            {
                measurement: 'lightning_board_snapshot_FX_BTC_JPY',
                fields: {
                    mid_price: Influx.FieldType.INTEGER,
                    bids: [
                        {
                            price: Influx.FieldType.INTEGER,
                            size: Influx.FieldType.FLOAT
                        }
                    ],
                    asks: [
                        {
                            price: Influx.FieldType.INTEGER,
                            size: Influx.FieldType.FLOAT
                        }
                    ]
                },
                tags: [
                ]
            }
        ]
    })

え?配列は登録できないの?

できないのである。

例えば上のような定義を行って次のコードでデータを送るとエラーだ。

なおデータはmessageという変数に格納されて送られてくるとする

  influx.writePoints([
      {
          measurement: 'lightning_board_snapshot_FX_BTC_JPY',
          tags: {},
          fields: {
              mid_price: message.mid_price,
              asks: [...message.asks], //asks: message.asks でももちろんだめ
              bids: [...message.bids],
          },
      }

スプレッド構文がだめとかそういうのは関係なくだめなのだ。

英語フォーラムにも同じ質問をしているページがあり「できないよ(1個づつ分け定義してね)」と解答されている。

どうしたらいいのか

今回は次の方法で対応という解決策しか浮かばなかった。

スキーマの定義

    schema: [
        {
            measurement: 'lightning_board_snapshot_FX_BTC_JPY',
            fields: {
                mid_price: Influx.FieldType.INTEGER,
                asks: Influx.FieldType.STRING,
                bids: Influx.FieldType.STRING,
            },
            tags: []
        }
    ]

asks と bids を単なる string と定義する。

データの登録

  const asks = JSON.stringify(message.asks)
  const bids = JSON.stringify(message.bids)    
  influx.writePoints([
      {
          measurement: 'lightning_board_snapshot_FX_BTC_JPY',
          tags: {},
          fields: {
              mid_price: message.mid_price,
              asks: asks,
              bids: bids,
          },
      }

JSON.stringify を使って強制的に文字列にした。

これによるデメリット

当然ながらChronograf等で可視化が不可能となる。またデータをリードして使用する時に JSON.parse による復元処理が必要になる。

実行結果:Chronografから参照

Chronograf

文字列として格納される。とりあえずは保存できている。

現状の考察

そもそも気づけば私自身データベースの設計などしたことがない。このあたりは実務でやってる人間とそうでないとで大きく分かれそうだ。

とりあえずデータは正しく記録さえできればそれでよいのであるが、これが最適解とは言い難い。

解決案:テーブルを分けるのはどうか

askとbidのテーブルに分けて { price, size } を1件としてバラバラにしたらどうか?

確かに時系列にさえなっていれば、データがバラバラでも再現時にシーケンシャルリードしてグラフにはめていくだけで良い。

試してみる価値はあるかもしれないが、データの件数がえげつないことになりそうなのでとりあえずやってない。

宿題

板データのような秒間に200件近く入ってくるレコードの登録(writePoints)は処理速度が間に合うのかどうか。

今回は時間の都合でここまでとする。

この記事をシェア:

author icon

仮想トイレ @CrypticToilet
プログラミングや仮想通貨のシステムトレードに関する情報を更新中!どんな情報を流しても詰まらないトイレ。