Litestream というSQLite3をレプリケーションするOSSを調べてみました
インストール
macだと以下でインストールできます
$ brew install benbjohnson/litestream/litestream
使い方
レプリケーション先は同一ホストの任意のパスやAmazon S3を利用できます。 今回はS3と互換性のあるオブジェクトストレージの MinIO を使って検証してみました。
MinIOの起動
$ docker run -p 9000:9000 minio/minio server /data
起動後は http://localhost:9000 でクレデンシャルに minioadmin
を指定してログインし、適当なバケットを作成します。
今回は mybkt
というバケットを作成しています。
Litestream起動前にsqlite3で適当なDBを作っておきます
$ sqlite3 test.db
CREATE TABLE hoge (id int primary key);
INSERT INTO hoge VALUES (1),(2);
Litestreamを起動します
$ export AWS_ACCESS_KEY_ID=minioadmin
$ export AWS_SECRET_ACCESS_KEY=minioadmin
$ litestream replicate test.db s3://mybkt.localhost:9000/test.db
これでtest.dbに対する変更がS3にレプリケーションされ、リストアできるようになります。
設定ファイル
コマンド引数で対象のSQLite3のDBファイルやレプリケーション先を指定できますが、設定ファイルを使うと複数DB・レプリケーション先を指定できます
access-key-id: minioadmin
secret-access-key: minioadmin
dbs:
- path: /path/to/test1.db
replicas:
- url: s3://mybkt.localhost:9000/test1.db
- path: /path/to/test2.db
replicas:
- path: /path/to/replica
- name: test2-1
url: s3://mybkt.localhost:9000/test2-1.db
- name: test2-2
url: s3://mybkt.localhost:9000/test2-2.db
nameは省略可能で、省略するとタイプの名前(s3
やfile
)が適用されます。
nameは各DBに対してユニークにする必要があるので、レプリカ先に同じタイプを複数利用する場合はnameを指定する必要があります。
DBリストア
コマンドラインでDBのリストアができます。
$ litestream restore -o test.back.db test.db
timestampオプションを使ってpoint-in-timeリカバリも可能です
$ litestream restore -o test.back.db \
-timestamp 2021-04-04T15:05:00+09:00 test.db
その他
レプリカ先ではこんな感じでファイルが作成されます。
$ tree replica
replica
└── generations
└── effef08113d5e035
├── snapshots
│ └── 00000000.snapshot.lz4
└── wal
├── 00000000.wal.lz4
├── 00000001.wal.lz4
└── 00000002.wal
また、Litestreamは WAL(write-ahead log) を利用するため、SQLite3のWALジャーナルモードでしか動きません。
仕組みについて
WALはコミットされた変更が入っていますが、肥大化を防ぐためにWALをoriginalのDBに書き戻して、WALファイルをflushする必要があります。 これをcheckpointと呼んでいて、WALが1000ページのスレッドサイズ超えた場合やアクティブな接続がない場合にcheckpointが発生するようになっています。
LitestreamではReadのトランザクションを張り続けてcheckpointされないようにして Litestream側から直接checkpointを操作することによってWALファイルのflush・書き戻しを制御しています。 さらにLitestreamではWALファイルをコピーしていて(shadow WAL)、以下のような階層のディレクトリ内にWALファイルを作成します。
$ tree .test.db-litestream
.test.db-litestream
├── generation
└── generations
└── 24c251214a5c01c7
└── wal
├── 00000007.wal
└── 00000008.wal
ある時間のsqlite3をsnapshotファイルとして残して snapshotからコピーしたWALを順に適用していくことで、任意の時刻のデータを復元できるようにしています。
snapshotとWALファイル郡のことをgenerationと呼んでいて、WALフレームで欠損があれば新しいgenerationを作っています。 これによって整合性の取れたgenerationが存在する状態にしています。
まとめ
Litestreamを使ってSQLite3のレプリケーション(ストリーミングなバックアップ+PITR)ができることを確認しました。 SQLite3は1ファイルでDB管理をしているのでLitestreamを使わずともファイルをコピーするだけでバックアップ・復元が可能ですが ファイルコピーによるバックアップはバックアップの数×データ量のストレージを使うためPITRをするにはデータ量の効率が悪いです。
Litestreamを使うとサーバー内のデータが消失した場合にもレプリケーション先から時間指定で復旧することができますし、 WALファイルベースでコピーしているため、おそらくデータ量の効率も良いと思われます。 また、アプリケーションコードをいじらずに、別プロセスでレプリケーションができるのも利点です。
リードレプリカに関しても搭載予定 とのことです…!