このプロジェクトはまだ初期段階にあり、本番環境に対応していないと考えてください。おそらく、さまざまな一貫性と正確性の問題があり、すべてのAPIが変更されます。
ArcticDBは、Goで記述された埋め込み可能な列型データベースです。半構造化スキーマ(型付きワイドカラムとしても説明できます)を備えており、ストレージにApache Parquetを使用し、クエリ時にApacheArrowを使用します。ArcticDBは、Apache Arrowの上に構築されており、クエリビルダーとさまざまなオプティマイザーを提供します(DataFrameのようなAPIを思い出させます)。
ArcticDBは、インタラクションの大部分が書き込みであるユースケース向けに最適化されており、データがクエリされると、一度に大量のデータがクエリされます(Polar Signalsのユースケースは、可観測性、特にParcaとして広く説明できます)。また、ワイドカラムの列型データベースとして説明することもできます。
アナウンスのブログ投稿を読んで、私たちがそれを作成した理由について学びましょう:https ://www.polarsignals.com/blog/posts/2022/05/04/introducing-arcticdb/
ArcticDBは、可観測性ワークロード用に特別に構築されました。これは、その組み合わせでそれをユニークにするいくつかの特徴をもたらしました。
目次:
可観測性データは、高次元であり、それらの次元を効率的に検索および集約できる場合に最も役立ちます。単一の行に属するすべてのデータを一緒にデータを格納する(MySQL、PostgreSQL、CockroachDB、TiDBなど)のような多くのリレーショナルデータベースとは異なり、列レイアウトでは、テーブル内の同じ列のすべてのデータが1つの連続したチャンクで利用できます。データをスキャンするのが非常に効率的になり、さらに重要なことに、クエリに本当に必要なデータのみが最初にロードされます。ArcticDBは、ストレージにApache Parquetを使用し、クエリ時にApacheArrowを使用します。Apache Parquetは、その効率的なエンコーディングを利用してメモリとディスクスペースを節約するためのストレージに使用されます。Apache Arrowは、クエリの実行をベクトル化するための基盤としてクエリ時に使用されます。
列データベースはすでに存在しますが、ほとんどの場合、静的スキーマが必要です。ただし、可観測性ワークロードは、スキーマが静的ではないという点で異なります。つまり、すべての列が事前定義されているわけではありません。一方、ワイドカラムデータベースもすでに存在しますが、通常は厳密に型指定されておらず、ほとんどのワイドカラムデータベースは行ベースのデータベースであり、カラム型データベースではありません。
Prometheusの時系列を例にとってみましょう。Prometheusの時系列は、ラベルセットの組み合わせによって一意に識別されます。
http_requests_total{path="/api/v1/users", code="200"} 12
ラベル名を事前に知ることができないため、このモデルは静的スキーマにうまくマッピングされません。一部の列データベースが提供する必要のある最も適切なデータ型はマップですが、マップには行ベースのデータベースと同じ問題があり、行内のマップのすべての値が一緒に格納され、列の利点を活用できません。レイアウト。ArcticDBスキーマでは、列を動的に定義して、新しいラベル名が表示されたときにその場で列を作成できます。
PrometheusのArcticDBスキーマは次のようになります。
package arcticprometheus
import (
"github.com/polarsignals/arcticdb/dynparquet"
"github.com/segmentio/parquet-go"
)
func Schema() *dynparquet.Schema {
return dynparquet.NewSchema(
"prometheus",
[]dynparquet.ColumnDefinition{{
Name: "labels",
StorageLayout: parquet.Encoded(parquet.Optional(parquet.String()), &parquet.RLEDictionary),
Dynamic: true,
}, {
Name: "timestamp",
StorageLayout: parquet.Int(64),
Dynamic: false,
}, {
Name: "value",
StorageLayout: parquet.Leaf(parquet.DoubleType),
Dynamic: false,
}},
[]dynparquet.SortingColumn{
dynparquet.NullsFirst(dynparquet.Ascending("labels")),
dynparquet.Ascending("timestamp"),
},
)
}
注:Prometheusは、タイムスタンプにダブルデルタエンコーディングを使用し、値にXORエンコーディングを使用することを認識しています。このスキーマは、動的列機能を強調するための単なる例です。
このスキーマでは、すべての行にa
timestampとa
valueが必要ですが、前に。が付いた列は異なる場合があります
labels.。このスキーマでは、動的に作成されたすべての列は引き続きディクショナリとランレングスでエンコードされており、タイプはである必要があります
string。
書き込みと読み取りのみがあります。すべてのデータは不変でソートされています。すべてのデータを並べ替えることで、ArcticDBは列ごとのインデックスの維持を回避し、低レイテンシでクエリを処理できます。
グローバルな並べ替えを維持するには、ArcticDBでは、複数の行が含まれている場合、すべての挿入を並べ替える必要があります。不変性と組み合わせることで、すべてのデータのグローバルな並べ替えを妥当なコストで維持できます。スループットを最適化するには、可能な限り大きなバッチで挿入を実行することをお勧めします。ArcticDBは、挿入されたデータを、Granuleと呼ばれる構成可能な行数(デフォルトでは8192)のバッチで維持します。クエリに必要なデータに直接ジャンプするために、ArcticDBはGranulesのスパースインデックスを維持しています。スパースインデックスはメモリに完全に常駐するのに十分小さいため、現在、Granulesのbツリーとして実装されています。
挿入時に、ArcticDBは挿入された行を下限と上限に従って適切なグラニュールに分割し、グローバルな並べ替えを維持します。
Nグラニュールが設定された量を超えると、グラニュールはそれに応じて新しいグラニュールに分割されます。
内部的には、グラニュールはソートされたパーツのリストであり、クエリで必要な場合にのみ、最小ヒープを使用した直接k-wayマージを使用して、すべてのパーツがソートされたストリームにマージされます。Granule全体を単一のソートされたストリームとして読み取る必要がある操作の例は、前述のGranule分割です。
ArcticDBにはスナップショットアイソレーションがありますが、十分に理解しておく必要のあるいくつかの注意事項があります。使用目的は、データを書き込むエンティティと同じではないデータを読み取るユーザー向けであるため、書き込み後の読み取りの一貫性はありません。新しいデータを表示するには、ユーザーはクエリを再実行します。書き込み後の読み取りの一貫性をトレードオフすることを選択すると、メカニズムでスループットを大幅に向上させることができます。ArcticDBは、書き込みトランザクションをバッチでリリースします。これは基本的に書き込みの原子性を保証するだけであり、読み取り時に書き込みが破損することはありません。データは不変であるため、これらの特性が合わさってスナップショットアイソレーションが発生します。
より具体的には、arcticDBは透かしを維持し、透かし以下のすべてのトランザクションが安全に読み取れることを示します。書き込みトランザクションのみが新しいトランザクションIDを取得し、読み取りは透かしのトランザクションIDを使用して、安全に読み取ることができるデータを識別します。透かしは、厳密に単調で連続したトランザクションが終了した場合にのみ増加します。これは、低い書き込みトランザクションが高い書き込みトランザクションをブロックして読み取り可能になる可能性があることを意味します。確実に進行させるために、書き込みトランザクションにはタイムアウトがあります。
このメカニズムは、 Google Spanner、Google Percolator、および高可用性トランザクションの組み合わせに触発されています。
ArcticDBは巨人の肩の上に立っています。素晴らしいライブラリを作成するためのSegmentと、ApacheArrowのGoサポートにparquet-go
取り組んだ後の開始およびさまざまな貢献者のためのInfluxDataに大声で叫んでください。