Closed27
XTDB version 2 を読みながらメモ
このスクラップはXTDB V2の勉強メモです。後でまとめて、Bookにしたり記事にしたりします。
- 2024/01/21:XTQLだけのBook作成:Clojureで XTDB v2 クエリ言語 XTQL を学ぶ
- XTDB とは
- Bitemporal, Dynamic Database
- SQLとXTQLでクエリ化
- 無料
- オープンソース
- MPL License
- 現在: HTTP APIとClojureSDKのみ利用可
- 将来:Java/Kotlin, JavaScript, Python SDKを絶賛ハードワーク中
スタンドアロンのDockerイメージで XTDB server をスタート
docker pull ghcr.io/xtdb/xtdb-standalone-ea
-
Error response from daemon: Head "https://ghcr.io/v2/xtdb/xtdb-standalone-ea/manifests/latest": denied: denied
エラーが出る場合はsudo
を付けてみてください。わたしはそれでうまく行きました。
docker run -tip 3000:3000 ghcr.io/xtdb/xtdb-standalone-ea
Starting XTDB 2.x (pre-alpha) @ "dev-SNAPSHOT" @ 7ef8b33 ...
20:55:49 | INFO xtdb.server | HTTP server started on port: 3000
20:55:49 | INFO xtdb.cli | Node started
...
- http://localhost:3000/ で サーバー起動
- 確認
curl http://localhost:3000/status
{"latest-completed-tx":null,"latest-submitted-tx":null}
- デフォルトでは、Dockerイメージ内のローカルディレクトリにデータが格納される
- ボリュームを自分のローカルマシンにマウントしたい場合
docker run \
-tip 3000:3000 \
-v /path/to/host/dir:/var/lib/xtdb \
ghcr.io/xtdb/xtdb-standalone-ea
- サーバーコンテナをストップするときは
ctrl + c
clojure で接続
deps.edn
{:mvn/repos {"ossrh-snapshots" {:url "https://s01.oss.sonatype.org/content/repositories/snapshots"}}
:deps {org.clojure/clojure {:mvn/version "1.11.1"}
com.xtdb/xtdb-api {:mvn/version "2.0.0-SNAPSHOT"}
com.xtdb/xtdb-http-client-clj {:mvn/version "2.0.0-SNAPSHOT"}}}
- REPL 起動
;; deps.edn があるディレクトリで
$ clj
- 接続
user=> (require '[xtdb.client :as xtc]
'[xtdb.api :as xt])
(with-open [node (xtc/start-client "http://localhost:3000")]
(xt/status node))
user=> {:latest-completed-tx nil, :latest-submitted-tx nil}
- XTDB は 'bitemporal' で 'dynamic' なリレーショナル・データベース
- (XTDB ver1 の時にRDBだとは言っていなかった)
- (dynamicの意味は、https://zenn.dev/link/comments/4a5c9e0588089a で説明 )
- XTDB は system time と varid time の2つの時間を保持し、追跡する
- system time: DB に挿入もしくは更新された時間
-
varid time: 特定のレコードが、アプリケーションのなかで「有効 / 無効」とみなされるタイミング
- レコード: 行、レコード、ドキュメントを指す
- この system と varid time という2つの時間次元の組み合わせを Bitemporality (バイテンポラリティ) と呼ぶ。
- (時間次元という言葉は、もう少し理解してから説明する)
- XTDBにデータを挿入すると、Bitemporalなデータとして格納される
- 「コラムの追加」「コラムの更新」などは考える必要は無い
- すべてのデータは時間というバージョニングが自動付加されている
-
time-versioning
されたデータは、過去のどの地点に対するヒストリカルデータに対しても簡単にアクセスすることが出来る - データベースでのすべての変更を簡単に 監査(audit) することを可能にしている
- つまり、データの完全な履歴を持っている
- これは開発者にとって、データがimmutableであることと同じ
- データの過去のどの修正に対してもアクセスすることが出来る
- すべてのテーブルはデフォルトで4つのカラムを持つ
- (詳しくは後で)
- これは自動でメンテナンスされる
- 特記されない限り、クエリは「現在」が基準になる
- 有効ではないヒストリカルデータは、内部でフィルターアウトされる
- アーキテクチャ
- カラム型データアーキテクチャ
- ストレージとコンピューティングを分離
- ApacheArrowや、オブジェクトストレージ(S3など)で構築
- 大量の履歴データ保持のための運用コストを下げるため
- Apache Arrow(アパッチ・アロー)は、異なるデータ処理フレームワークや言語間での効率的なデータの交換を目的としたオープンソースのソフトウェアフレームワーク
- XTDBクラスタ内のすべてのノードは、
Write-Ahead Log
を読み込むレプリカ - このLogは一つしか存在せず、すべてのノードに共有されている
- このデザインの主な利点は、DBがいつ、どのように、なぜ、変更されたのかを正確に示す具体的な情報を保証出来ること
- スループットに厳しい上限が生じる可能性はある
- Dynamic relational engine
- XTDBの columnar エンジンは、
- 前もってスキーマを設計する必要はない
- XTDB はデータを
documents
として表現 -
documents
は、無制限の行 (wide rows) を持つ、疎なテーブルと捉えて良い -
documents
は、任意の入れ子データを含むことが出来る - 入れ子構造のなかで、すべての組み込み型がサポートされている
- 開発者は、XTDBをドキュメントストアとして扱うことができると同時に、伝統的な正規化されたデータベースとして扱うことも出来る。
- クエリ言語:2つのクエリ言語が同時に使える
- SQL:主にデータ分析用
-
SQL:2011 。これは標準SQLとに始めてビットタイム機能を導入したもの。XTDBでもこちらが
first-class citizen
(どういう意味?何となくわかるけど)
-
SQL:2011 。これは標準SQLとに始めてビットタイム機能を導入したもの。XTDBでもこちらが
- XTQL:主に開発者の生産性を上げるため
- JSON APIを使ってクライアントで記述出来る、コンポーザブルな形式を取れる用に拡張した新しいRDB言語
- SQL:主にデータ分析用
- 2つの言語を相互利用することで、分析用途であっても、アプリケーション機能開発であっても、その都度適切なAPIを使用すればよく、犠牲や妥協を強いられない。
- XTQL
- データ指向型クエリ言語
- Datalogと relational algebra に理論的基盤を置く
- Datalog
- 論理プログラミング言語(主にDB問い合わせ用言語)
- 論理述語論理(predicate logic)に基礎
- Prologなどの論理プログラミング言語の基盤
- Relational algebra
- リレーショナルデータベースの理論的基盤
- 集合論のアイディアに基礎
- データベースの操作(選択、射影、結合など)を集合論的な操作として表現
- SQLクエリ言語
- Datalog
- XTQLはクエリにもトランザクションにも使える
- https://docs.xtdb.com/intro/what-is-xtql.html
- 'Operators' と 'relations'
- XTQL は演算子(operators)で構成されている
- 開発者はそれ「組み合わせ」(composable)して使う
- パイプラインを使って大きなクエリを行う
- Source オペレータ
read from a-table
- (memo: each yield a 'relation' - an unordered bag of rows の翻訳はキモなので実際に触ってから訳する)
- https://docs.xtdb.com/reference/main/xtql/queries.html#_source_operators
- Tail オペレータ
- relation をフィルターする、追加フィールドを演算する
- ある関係性から、新しい関係性や関係性の変換を行うオペレータ
- https://docs.xtdb.com/reference/main/xtql/queries.html#_tail_operators
- XTQL は演算子(operators)で構成されている
- 実際に動かしてみる
- A First Look at XTDB v2 - live show & tell - YouTube でTutorialとして準備されているレポジトリを使う
- YOUTUBE と README のご指南に従って起動を試みる
git clone git@github.com:xtdb/sakila-playground.git
cd sakila-playground/
clj -M:xtdb:nrepl
- 例外発生
Caused by: java.lang.UnsupportedClassVersionError: xtdb/IArrowWriter has been compiled by a more recent version of the Java Runtime (class file version 61.0), this version of the Java Runtime only recognizes class file versions up to 55.0
- わたしの環境
$ cat /etc/os-release
NAME="Linux Mint"
VERSION="21.2 (Victoria)"
$ clj --version
Clojure CLI version 1.11.1.1155
$ java -version
openjdk version "11.0.21" 2023-10-17
- よーくさがすと
deps.edn
にコメントで
;; needed on JDK16+
と書いてある。(READMEとかにかいてよー。)
- jdk を更新
# OpenJDKパッケージの利用可能なバージョンを確認する
apt search openjdk
# openjdk-xx-jdk で利用できる一番新しそうなのは、openjdk-21-jdk だったので、
sudo apt install openjdk-21-jdk
sudo apt install openjdk-21-jre # 念の為やってみたけど↑で最新にしてくれてた
- 再度チュートリアルを起動
clj -M:xtdb:nrepl
rlwrap: warning: could not set locale
warnings can be silenced by the --no-warnings (-n) option
17:37:06 | DEBUG user | Submitting 'actor' table
17:37:07 | DEBUG user | Submitting 'address' table
17:37:07 | DEBUG user | Submitting 'category' table
17:37:07 | DEBUG user | Submitting 'city' table
17:37:07 | DEBUG user | Submitting 'country' table
17:37:07 | DEBUG user | Submitting 'customer' table
17:37:07 | DEBUG user | Submitting 'film' table
17:37:07 | DEBUG user | Submitting 'film_actor' table
17:37:07 | DEBUG user | Submitting 'film_category' table
17:37:07 | DEBUG user | Submitting 'inventory' table
17:37:07 | DEBUG user | Submitting 'language' table
17:37:07 | DEBUG user | Submitting 'payment' table
17:37:07 | DEBUG user | Submitting 'rental' table
17:37:08 | DEBUG user | Submitting 'staff' table
17:37:08 | DEBUG user | Submitting 'store' table
17:37:08 | INFO user | Sakila playground started!
nREPL server started on port 35497 on host localhost - nrepl://localhost:35497
- 🤩
src/user.clj
- nREPL に接続して、 user namespace の xt-node を評価すると
resources/sakila
に入っている テストデータ(どこかのレンタルビデオ屋の情報らしい)が submit される -
submit-file!
関数でtable-name
にファイル名がバインドされているのクエリのテーブル名はファイル名を使うと良い
- https://docs.xtdb.com/intro/what-is-xtql.html
-
from
オペレータ- Source operator の一つ
- XTDBのテーブルからデータを読み込む
- query
(from :customer [first_name last_name])
-
(from テーブル名 [コラム名, ...])
-
:customer テーブルから、first_name last_name に対応する :first_name :last_name を取得する
-
クエリ実行の文法
(xt/q node クエリをクオートで渡す)
- 実行
(xt/q xt-node
'(from :customer [first_name last_name]))
;; [{:last-name "BROWN", :first-name "ELIZABETH"}
;; {:last-name "YOUNG", :first-name "CYNTHIA"}
;; {:last-name "BURKE", :first-name "LYDIA"} .....
;; ]
- Pipeline
- pipeline 演算子にわたすことで結果行を変換できる
- n件取得, コラム指定無し
(xt/q xt-node
'(-> (from :customer [*])
(limit 3)))
;; [{:xt/valid-from #time/zoned-date-time "2023-12-26T10:44:13.640116Z[UTC]", :address-id 9, :email "ELIZABETH.BROWN@sakilacustomer.org", :last-name "BROWN", :store-id 1, :first-name "ELIZABETH", :active true, :xt/id 5}
;; {:xt/valid-from #time/zoned-date-time "2023-12-26T10:44:13.640116Z[UTC]", :address-id 32, :email "CYNTHIA.YOUNG@sakilacustomer.org", :last-name "YOUNG", :store-id 1, :first-name "CYNTHIA", :active true, :xt/id 28}
;; {:xt/valid-from #time/zoned-date-time "2023-12-26T10:44:13.640116Z[UTC]", :address-id 247, :email "LYDIA.BURKE@sakilacustomer.org", :last-name "BURKE", :store-id 1, :first-name "LYDIA", :active true, :xt/id 243}]
ここでの from に関する説明だと、
XTQL queries consist of composable operators, optionally combined with a pipeline, but this instance we only have from which is a "source" operator that retrieves a relation from a table stored in XTDB.
- from は ソース演算子
- from は xtdb に格納されたテーブルから、「a relation」を取得するために使用する
relation を取得するってなんだ?
- 脱線
- Clojure Slack の #biff チャンネルで見つけたXTDBのV1とV2の話
- https://clojurians.slack.com/archives/C013Y4VG20J/p1703343121735409?thread_ts=1703327712.449499&cid=C013Y4VG20J
- この議論によると、
- version1 と version2 のクエリ言語は根本的に違う
- この違いは「改善」だと思っている
- v1 はオープンソースであるし、サポートもするしバグ修正もする
- ということで、少なくともクエリに関しては互換性はなさそう
- V1を使い続けても良さそう
-
unify
- 複数テーブルをJoin
- たとえば以下のSQLをXTQLで表現すると
SELECT c.customer_name, o.xt$id AS order_id, o.order_value
FROM customers c
JOIN orders o ON (o.customer_id = c.xt$id)
(-> (unify (from :customers [{:xt/id customer-id} customer-name])
(from :orders [{:xt/id order-id} customer-id order-value]))
(return customer-name order-id order-value))
sakila
データの customer
と address
を見ると
customer
{:email "MARY.SMITH@sakilacustomer.org", :first_name "MARY", :xt/valid-from #time/instant "2006-02-14T22:04:36Z", :active true, :last_name "SMITH", :address_id 5, :xt/id 1, :store_id 1}
{:email "PATRICIA.JOHNSON@sakilacustomer.org", :first_name "PATRICIA", :xt/valid-from #time/instant "2006-02-14T22:04:36Z", :active true, :last_name "JOHNSON", :address_id 6, :xt/id 2, :store_id 1}
{:email "LINDA.WILLIAMS@sakilacustomer.org", :first_name "LINDA", :xt/valid-from #time/instant "2006-02-14T22:04:36Z", :active true, :last_name "WILLIAMS", :address_id 7, :xt/id 3, :store_id 1}
address
{:address "47 MySakila Drive", :district "Alberta", :city_id 300, :xt/id 1}
{:address "28 MySQL Boulevard", :district "QLD", :city_id 576, :xt/id 2}
{:address "23 Workhaven Lane", :district "Alberta", :city_id 300, :phone "14033335568", :xt/id 3}
なので address テーブルの :xt/id を address-id としてバインドして、
customer テーブルの address-id で join する
(xt/q xt-node
'(-> (unify (from :customer [address-id first-name])
(from :address [{:xt/id address-id} district address]))
(return first-name district)
(order-by district)
(limit 3)))
[{:first-name "TOM", :district "Abu Dhabi"}
{:first-name "LILLIAN", :district "Abu Dhabi"}
{:first-name "JORGE", :district "Aceh"} ]
- Transaction
- 文法:
(xt/submit-tx <node> <tx-ops> <opts>?)
- <tx-ops> : transaction オペレータのベクタ
- <opts> :
- system-time:トランザクションの時間を書き換える
- default-tz:デフォルトタイムゾーンを上書きする
- 文法:
- transaction オペレータ
- put
- insert-into
- update-table
- delete
- delete-from
- erase
- erase-from
- Asserts
- call
- put
- table にドキュメントをUpsertする
(xt/put <table> <document>)
- 返り値: xtdb.tx.Put
(xt/put
:person
{:person/name "James Cameron",
:person/born #inst "1954-08-16T00:00:00.000-00:00",
:xt/id 100})
#xt.tx/put {:table-name :person, :doc {:person/name "James Cameron", :person/born #inst "1954-08-16T00:00:00.000-00:00", :xt/id 100}, :valid-from nil, :valid-to nil}
これをベクタにいれてノードに submit する
(def my-node (xtn/start-node {}))
(xt/submit-tx
my-node
[(xt/put :person
{:person/name "James Cameron",
:person/born #inst "1954-08-16T00:00:00.000-00:00",
:xt/id 100})])
;; #xt/tx-key {:tx-id 0, :system-time #time/instant "2023-12-31T08:28:27.838133Z"}
query
(xt/q my-node '(from :person [*]))
;; [{:person/born #time/zoned-date-time "1954-08-16T00:00Z[UTC]", :person/name "James Cameron", :xt/id 100}]
options
(def my-node (xtn/start-node {}))
(xt/submit-tx
my-node
[(xt/put :person
{:person/name "James Cameron",
:person/born #inst "1954-08-16T00:00:00.000-00:00",
:xt/id 100})]
{:system-time #inst "2018-06-01"}) ;; :default-tz は説明書もテストも見つからず
;; #xt/tx-key {:tx-id 0, :system-time #time/instant "2018-06-01T00:00:00Z"}
https://docs.xtdb.com/static/learn-xtql-today-with-clojure.html にある my-movies
と my-persons
をローカルにコピーして使う
(xt/submit-tx
my-node
(for [doc my-persons]
(xt/put :persons doc)))
(xt/submit-tx
my-node
(for [doc my-movies]
(xt/put :movies doc))
(xt/q my-node
'(-> (from :movies [movie/title movie/year])
(limit 3)))
;; [#:movie{:year 1986, :title "Aliens"} #:movie{:year 1988, :title "Die Hard"} #:movie{:year 1988, :title "Rambo III"}]
このスクラップは2024/02/19にクローズされました