😎

テーブル比較ツール、data-diff使ってみた

2022/12/17に公開

気にはなってるけど触ってないビッグデータ系のツール・サービスを触る 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. テーブルを分割(セグメント)
  2. セグメント毎にチェックサムを計算・比較
  3. チェックサムが同じなら終わり
  4. チェックサムが違う場合は、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