テーブル比較ツール、data-diff使ってみた
気にはなってるけど触ってないビッグデータ系のツール・サービスを触る Advent Calendar 2022の#17です。
tl;dr
- テーブル同士を比較するツールだよ
- 違う種類のデータベースも比較できるよ
data-diffとは
公式ドキュメント曰く、
data-diff enables data professionals to detect differences in values between any two tables.
です。つまり、二つのテーブルのデータを比較して、違いを確認するツールです。
ユースケースとしては、
- データ移行時の確認
- データパイプラインでの確認
- データパイプラインのデバッグ
- データ欠損の確認
などが想定されているようです。
開発を行っているDatafoldはデータ品質系のSaaSを提供している企業ですが、そのうちデータの比較機能(の一部)をOSSとして公開してくれているのが、このdata-diffです。
Datafoldについて気になる方は、クラスメソッドさんの記事などをご覧になってください。
対応データベース
- PostgreSQL
- MySQL
- Snowflake
- bigQuery
- Redshift
- Oracle
- Presto・Trino
- DataBricks
- ClickHouse
- Vertica
- DuckDB
今回はPostgreSQLとDuckDBで試してみました。
仕組み
以下の流れで、二つのテーブルの間の比較を行っているようです。小さい単位に分割して比較を行っているので、data-diffを実行しているマシンのメモリ以上のテーブルの比較や、並列化による高速化が行えそうです。
- テーブルを分割(セグメント)
- セグメント毎にチェックサムを計算・比較
- チェックサムが同じなら終わり
- チェックサムが違う場合は、1~3をセグメントに対し繰り返し、違いが生じているレコードを見つける
詳しくは、Techincal Explanationをご参照してください。
使ってみる
Ubuntu 20.04 (Windows10のWSL2上)、minikube kubernetes-version v1.23.8で試しました。
インストール
data-diff本体と、DuckDB・PostgreSQL用のプラグインをインストールします(データベース毎のプラグインがあります)。
pip install data-diff
# DuckDB関係のインストール
pip install data-diff[duckdb]
# PostgreSQL関係のインストール
sudo apt install libpq-dev
pip install psycopg2-binary 'data-diff[postgresql]'
DuckDB
まずは比較対象のデータベース・テーブルを二つ作ります。DuckDBとは?については、🦆🦆🦆🦆🦆🦆DuckDB入門🦆🦆🦆🦆🦆🦆などをご参考してください。
duckdb duck_sample.db
CREATE TABLE t1(key STRING, not_key STRING, ignore_column FLOAT);
INSERT INTO t1 VALUES ('common_key', 'common', random()), ('changed_key', 'before', random()), ('deleted_key', 'deleted', random());
duckdb duck_sample2.db
CREATE TABLE t1(key STRING, not_key STRING, ignore_column FLOAT);
INSERT INTO t1 VALUES ('common_key', 'common', random()), ('changed_key', 'changed', random()), ('inserted_key', 'inserted', random());
二つのテーブルをignore_column以外で比較すると、
- keyカラム=changed_keyのレコードは、両方のテーブルに存在。not_keyの値は違う
- keyカラム=deleted_keyのレコードは、duck_sample1だけに存在
- keyカラム=inserted_keyのレコードは、duck_sample2だけに存在
の三点の違いがあります。
data-diffを使って上の違いを検知できるか、確認してみます。
data-diff 'duckdb://main@./duck_sample.db' t1 'duckdb://main@./duck_sample2.db' t1 -k key -c not_key
- changed_key, before
+ changed_key, changed
- deleted_key, deleted
+ inserted_key, inserted
違いがある三つのレコードが検知出来ていそうですね。
オプションでは、
- 比較対象のデータベース(第1、3引数)
- 比較対象のテーブル(第2、4引数)
- 主キーカラム(同じレコードとみなすカラム)(kオプション)
- 値を比較するカラム(cオプション)
を指定します。kオプションにもcオプションにも指定していないカラムは無視されます(今回のテーブルのignore_columnの部分)。
オプションの詳細はOptionsを参照してください。今回は使っていませんが、テーブルのフィルターや、テーブルサイズに応じたチューニングなども可能です。
DuckDBとPostgreSQL
先ほどはDuckDBのテーブル同士を比較しました。異なる種類のデータベースを比較する例として、DuckDBとPostgreSQLのテーブルを比較してみます。
まずは比較対象のPostgreSQLを用意します。今回はコンテナイメージを使いますので、docker-composeファイルを記載します。
version: '3.1'
services:
db:
image: postgres:15.1
restart: always
environment:
POSTGRES_PASSWORD: admin
ports:
- 5433:5432
volumes:
- ./postgresql:/var/lib/postgresql/data
# 動作確認用のツール。無くても動くはず
adminer:
image: adminer
restart: always
ports:
- 8080:8080
コンテナを起動します。
docker-compose up
ローカルのpsqlで接続し、データベースとテーブルを作成します。比較対象のカラム以外はスキーマが違っても大丈夫なことを確認するために、ignore_columnを削っています。
psql -h 127.0.0.1 -p 5433 -U postgres
CREATE TABLE t1(key varchar, not_key varchar);
INSERT INTO t1 VALUES ('common_key', 'common'), ('changed_key', 'changed'), ('inserted_key', 'inserted');
data-diff実行してみます。
data-diff 'duckdb://main@./duck_sample.db' t1 'postgresql://postgres:admin@localhost:5433/postgres' t1 -k key -c not_key
- changed_key, before
+ changed_key, changed
- deleted_key, deleted
+ inserted_key, inserted
+ inserted_key, inserted
違いが抽出されていそうですね(insertedが二回出る理由は不明…)。
設定ファイル
これまはコマンドラインオプションにデータベースの情報を記載していましたが、TOMLファイルに設定することも可能です。
datadiff.tomlファイルに設定を記載してみます。
[database.postgres]
driver = "postgresql"
database = "postgres"
host = "localhost"
user = "postgres"
password = "admin"
port = 5433
[database.duckdb]
driver = "duckdb"
database = "main"
filepath = "/home/notrogue/project/datadiff/duck_sample.db"
[run.default]
verbose = true
[run.analytics]
# Source 1 ("left")
1.database = "duckdb"
1.table = "t1"
2.database = "postgres"
2.table = "t1"
verbose = false (datadiff)
コマンドラインオプションで実行したのと、同様の結果を得ることが出来ます。
data-diff --conf datadiff.toml --run analytics -k key -c not_key
- changed_key, before
+ changed_key, changed
- deleted_key, deleted
+ inserted_key, inserted
+ inserted_key, inserted
Discussion