話題の記事

SQLiteを分散データベースに変えるmvSQLite

2022.08.26

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

Introduction

先日Githubに公開されたmvSQLiteですが、
「SQLite互換のスケーラブルなデータベース」とのことで話題になってます。

mvSQLiteの特徴は、SQLiteのストレージレイヤーをFoundationDBに分離しているところです。   これにより、DynamoDBのように際限のないスケーラビリティ、point-in-timeでの読み取り、
そしてRDBの厳密な一貫性を提供します。
作成者曰く、mvSQLiteの目標は
「SQLiteを分散データベースに変えること」
とのことです。

FoundationDBとは

FoundationDBは大量の構造化データを処理するために設計された分散データベースです。
2015年にAppleが買収したことでもニュースになりました。

データをソート済みのKeyValueデータとして保管し、
すべての操作にACIDトランザクションを適用するのが特徴です。
読み込み・書き込みのワークロードに適しており、
特に書き込みを多用するワークロードに
優れたパフォーマンスを発揮するとのことです。
1コアで20,000書き込み/秒のスループット(SSDを使う場合)を発揮するとか、
500コアまでリニアにスケール可能とか、大規模なシステムを想定して設計されています。

ちなみにDBとの接続はAPIの言語バインディングを使用して行います。
こんなのとか。

mvSQLite features

SQLiteと完全互換

mvSQLiteでは、カスタムVFSレイヤもしくはFUSE(選択可能)を使ってSQLiteと統合されます。
SQLiteの下層レイヤーとして動作するため、SQLiteすべての機能が使用可能。

スケーラブルな読み込み・書き込み

FoundationDBのメリット
(スケーラブルな分散トランザクション、同期/非同期レプリケーションなど)を
取り入れつつ、FoundationDBがサポートしていない5秒以上のトランザクションや
大きいサイズのトランザクションが可能になっています。

Drop-in addition

環境変数設定(LD_PRELOAD=libmvsqlite_preload.soもしくはFUSE)することで
既存のSQLiteベースのアプリを動作させることが可能

ACIDより厳しい制約

mvSQLiteはACIDよりも厳密な保証を提供します。
外部整合性なのでトランザクション処理システムにとって
最も厳密な整合性レベルを採用しています。

また、FoundationDBは同期レプリケーションを行って耐久性を保証します。
必要に応じて、グローバルな結果整合性のある低レイテンシーの
読み取りを実行するオプションも持っており、
さまざまなリージョンへの非同期レプリケーション (DR) を行うことも可能になってます。

ポイントインタイムでの読み取り

mvSQLiteはMVCC (マルチバージョン同時実行制御) をサポートしています。
過去のどの時点でもデータベースのスナップショットを開いて読み取ることができます。

Setup

mvSQLiteを動かしてみましょう。
環境は、EC2でubuntu
(ubuntu/images/hvm-ssd/ubuntu-jammy-22.04-amd64-server-20220609)のAMI
を使用してインスタンスを起動しました。

sshログインして作業開始。

% ssh -i <ssh key> ubuntu@<IPアドレス>

最初に必要なツール郡をインストールしておきましょう。  

% sudo apt-get update 
% sudo apt-get install -y wget
% sudo apt-get install -y zip
% sudo apt-get install -y gcc

ここにある手順でインストールを行います。
まずはFoundationDBのインストール。

% wget https://github.com/apple/foundationdb/releases/download/7.1.15/foundationdb-clients_7.1.15-1_amd64.deb
% sudo dpkg -i foundationdb-clients_7.1.15-1_amd64.deb
% wget https://github.com/apple/foundationdb/releases/download/7.1.15/foundationdb-server_7.1.15-1_amd64.deb
% sudo dpkg -i foundationdb-server_7.1.15-1_amd64.deb

必要なファイルをダウンロードして実行。

% curl -L -o ./libmvsqlite_preload.so https://github.com/losfair/mvsqlite/releases/download/v0.1.15/libmvsqlite_preload.so
% curl -L -o ./mvstore https://github.com/losfair/mvsqlite/releases/download/v0.1.15/mvstore

% RUST_LOG=info ./mvstore \
  --data-plane 127.0.0.1:7000 \
  --admin-api 127.0.0.1:7001 \
  --metadata-prefix mvstore-test \
  --raw-data-prefix m

  2022-08-25T06:12:14.575825Z  INFO mvstore: server initialized
    at mvstore/src/main.rs:173

  2022-08-25T06:12:14.582580Z  INFO mvstore::server: timekeeper started
    at mvstore/src/server.rs:651
% ps -ax | grep mvstore
   2582 pts/0    Sl+    0:00 ./mvstore --data-plane 127.0.0.1:7000 --admin-api 127.0.0.1:7001 --metadata-prefix mvstore-test --raw-data-prefix m

起動したみたいなのでOK。

Admin APIをつかってnamespaceを作ってみる。

% curl http://localhost:7001/api/create_namespace -i -d '{"key":"test","metadata":""}'
HTTP/1.1 200 OK
content-length: 2
date: Thu, 25 Aug 2022 06:13:32 GMT

ok

libsqlite3とsqlite3 CLIをビルドします。

% wget https://www.sqlite.org/2022/sqlite-amalgamation-3390200.zip
% unzip sqlite-amalgamation-3390200.zip
% cd sqlite-amalgamation-3390200
% gcc -O2 -fPIC --shared -o libsqlite3.so ./sqlite3.c -lpthread -ldl -lm
% gcc -O2 -o sqlite3 ./shell.c -L. -lsqlite3

Githubにある手順だとここで環境変数を指定してシェルを実行しているのですが、
私の環境だとlibssl.so.1.1がないとかエラーになったので、
↓の手順でopensslをビルドして回避。
もっといい方法があると思いますが、とりあえず動かすだけなのでよしとする。

% wget https://www.openssl.org/source/openssl-1.1.1o.tar.gz
% tar -zxvf openssl-1.1.1o.tar.gz
% cd openssl-1.1.1o
% ./config
% make
% make test
% sudo make install

改めて、環境変数の設定とシェルの起動。
LD_LIBRARY_PATHに設定しているopenssl-1.1.1oは、
↑でビルドしたパスを指定しました。

% export RUST_LOG=info MVSQLITE_DATA_PLANE="http://localhost:7000"
% LD_PRELOAD=/path/your/libmvsqlite_preload.so LD_LIBRARY_PATH=.:/path/your/openssl-1.1.1o ./sqlite3 test

これでsqliteのシェルが表示されます。
動かしてみましょう。
まずは適当なテーブルをcreateしてみます。

SQLite version 3.39.2 2022-07-21 15:24:47
Enter ".help" for usage hints.
sqlite> create table user (id integer primary key,name text not null);
  2022-08-25T06:39:59.696059Z  INFO mvsqlite: mvsqlite initialized, sector_size: 8192
    at mvsqlite/src/lib.rs:76

  2022-08-25T06:39:59.696468Z  WARN mvfs::vfs: read_exact_at called without a transaction, offset: 0, len: 100
    at mvfs/src/vfs.rs:316

  2022-08-25T06:39:59.709848Z  INFO mvfs::vfs: transaction committed, version: "000000006d786fc50000", duration: 3.943723ms, num_pages: 2, rea

d_version: "00000000000000000000", last_version: "00000000000000000000"
    at mvfs/src/vfs.rs:527

なんかログ出てます。
トランザクションがコミットされるとその時点でのバージョンが表示されてますね。
適当なデータをinsertしてselectしてみます。

sqlite> insert into user values (1,'syuta');
  2022-08-25T06:40:56.246544Z  INFO mvfs::vfs: identity write ignored, page: 0
    at mvfs/src/vfs.rs:374

  2022-08-25T06:40:56.253111Z  INFO mvfs::vfs: transaction committed, version: "0000000070d7391b0000", duration: 3.420705ms, num_pages: 1, read_version: "000000006d786fc50000", last_version: "000000006d786fc50000"
    at mvfs/src/vfs.rs:527

sqlite> insert into user values (2,'taro');
  2022-08-25T06:41:22.843304Z  INFO mvfs::vfs: identity write ignored, page: 0
    at mvfs/src/vfs.rs:374

  2022-08-25T06:41:22.889989Z  INFO mvfs::vfs: transaction committed, version: "00000000726daafa0000", duration: 3.63283ms, num_pages: 1, read_version: "0000000070d7391b0000", last_version: "0000000070d7391b0000"
    at mvfs/src/vfs.rs:527

sqlite> select * from user;
1|syuta
2|taro

普通にSQLite使ってるのとまったく同じです。

Time travel(checkout past snapshots)を試してみる

ここでシェルを一旦終了し、
「1レコードコミットした時点でのバージョン」を指定して再度つなげてみます。
バージョンIDは「0000000070d7391b0000」なので、
それを指定して(DB名@<トランザクションID)シェルを起動します。

LD_PRELOAD=/path/your/libmvsqlite_preload.so LD_LIBRARY_PATH=.:/path/your/openssl-1.1.1o ./sqlite3 test@0000000070d7391b0000
SQLite version 3.39.2 2022-07-21 15:24:47
Enter ".help" for usage hints.
sqlite> select * from user;
  2022-08-25T06:51:47.898922Z  INFO mvsqlite: mvsqlite initialized, sector_size: 8192
    at mvsqlite/src/lib.rs:76

  2022-08-25T06:51:47.899325Z  WARN mvfs::vfs: read_exact_at called without a transaction, offset: 0, len: 100
    at mvfs/src/vfs.rs:316

1|syuta

データを確認すると、たしかにその時点でのデータになってます。

Summary

というわけで、mvSQLiteを動かしてみました。
現在はβ版でバグもありますが、
SQLite完全互換でスケーラブルな分散DBということで
今後が楽しみです。

References