SQLiteはファイル一つで使えて手軽なデータベースです。ちょっとしたWebアプリケーションであれば十分に耐えられますし、ファイル一つとあってバックアップも簡単に行えます。

そんなSQLiteをより便利に活用できるようにするのがrqlite です。通常のSQLiteに対してクラスタリングによるスケールやHTTP(S)によるアクセスを可能にします。

インストール

インストールは自分でコンパイルするか、Mac OSXやLinuxの場合はバイナリがダウンロードできます。

# Mac OSXの場合
curl -L https://github.com/rqlite/rqlite/releases/download/v3.5.0/rqlited-v3.5.0-darwin-amd64.tar.gz -o rqlited-v3.5.0-darwin-amd64.tar.gz
tar xvfz rqlited-v3.5.0-darwin-amd64.tar.gz
cd rqlited-v3.5.0-darwin-amd64

# Linuxの場合
curl -L https://github.com/rqlite/rqlite/releases/download/v3.5.0/rqlited-v3.5.0-linux-amd64.tar.gz -o rqlited-v3.5.0-linux-amd64.tar.gz
tar xvfz rqlited-v3.5.0-linux-amd64.tar.gz
cd rqlited-v3.5.0-linux-amd64

rqlitedの起動

まず最初はサーバを立ち上げます。実行ファイルはrqlitedです。適当なディレクトリを作成して、そこを指定して実行します。

$ ./rqlited node.1/

            _ _ _           _
           | (_) |         | |
  _ __ __ _| |_| |_ ___  __| |
 | '__/ _  | | | __/ _ \/ _  |
 | | | (_| | | | ||  __/ (_| |
 |_|  \__, |_|_|\__\___|\__,_|
         | |
         |_|

[rqlited] 2016/09/23 09:59:48 rqlited starting, version v3.5.0, commit 6b4d306160e300cc6813c548812f357e76093288, branch master
[tcp] 2016/09/23 09:59:48 mux serving on 127.0.0.1:4002, advertising 127.0.0.1:4002
  :
[cluster] 2016/09/23 09:59:48 service listening on 127.0.0.1:4002
  :
[rqlited] 2016/09/23 09:59:49 set peer for 127.0.0.1:4002 to localhost:4001
[http] 2016/09/23 09:59:49 service listening on localhost:4001

デフォルトではオンメモリデータベースになります。ファイルに書き出す場合は -ondisk を追加してください。実行すると、 HTTPサーバが 4001 番ポートで、クラスタリングのためのRaftサーバが 4002 で立ち上がります。最初に起動したサーバがリーダーになります。

SQLの実行

rqliteに接続する時には rqlite コマンドを使います。これはSQLiteのクライアントと変わらない使い勝手です。

$ ./rqlite
127.0.0.1:4001> CREATE TABLE foo (id INTEGER NOT NULL PRIMARY KEY, name TEXT)
0 row affected (0.000449 sec)
127.0.0.1:4001> INSERT INTO foo(name) VALUES("fiona")
1 row affected (0.000396 sec)
  :
127.0.0.1:4001> INSERT INTO foo(name) VALUES("test4")
1 row affected (0.000060 sec)

テーブルの作成、データの追加や更新、削除は特に問題なく行えます。トランザクションもサポートされています。

クラスタリング

スケールさせるためのクラスタリングは次のように行います。リーダーを指定して(joinオプション)起動します。

$ rqlited -http localhost:4003 -raft localhost:4004 -join http://localhost:4001 ./node.2
$ rqlited -http localhost:4005 -raft localhost:4006 -join http://localhost:4001 ./node.3

これだけでクラスタリングができます。なお、リーダーになるサーバを落とすと自動的に別なサーバがリーダーになります。

HTTPでのアクセス

rqliteではデータベースへHTTPからアクセスができます。SQLがそのまま実行できます。データの追加はPOSTで行います。

$ curl -XPOST 'localhost:4001/db/execute?pretty&timings' -H "Content-Type: application/json" -d '[
>     "INSERT INTO foo(name) VALUES(\"fiona\")"
> ]'
{
    "results": [
        {
            "last_insert_id": 6,
            "rows_affected": 1,
            "time": 6.18e-05
        }
    ],
    "time": 0.0024990610000000003
}

取得はGETです。

curl -G 'localhost:4003/db/query?pretty&timings' --data-urlencode 'q=SELECT * FROM foo'
{
   "results": [
       {
           "columns": [
               "id",
               "name"
           ],
           "types": [
               "integer",
               "text"
           ],
           "values": [
               [
                   1,
                   "fiona"
               ],
               [
                   2,
                   "test"
               ],
               :
          ]
      }
  ]
}

HTTPアクセスからでもトランザクションをサポートしています。ただし、その場合はSQLをまとめて送信する必要があります。つまり、一つ目のSQLの結果を受けて、その内容を使って次のSQLを構築することはできないようです。

$ curl -XPOST 'localhost:4001/db/execute?pretty&transaction' -H "Content-Type: application/json" -d "[
>     \"INSERT INTO foo(name) VALUES('fiona')\",
>     \"INSERT INTO foo(name) VALUES('sinead')\"
> ]"
{
    "results": [
        {
            "last_insert_id": 8,
            "rows_affected": 1
        },
        {
            "last_insert_id": 9,
            "rows_affected": 1
        }
    ]
}

SQLエラーがあった場合もちゃんと確認できます。

$ curl -XPOST 'localhost:4001/db/execute?pretty&timings' -H "Content-Type: application/json" -d "[
>     \"INSERT INTO nonsense\"
> ]"
{
    "results": [
        {
            "error": "near \"nonsense\": syntax error"
        }
    ],
    "time": 0.0025317670000000003
}

もちろん、このWeb APIに外部ブラウザなどから直接アクセスさせるのは危険なので止めましょう。rqlite向けにはまだプログラミング言語用のライブラリが多くないので、Web API経由でアクセスする方が使いやすいでしょう。

クラスタリングとの組み合わせ

Web APIを使ってポート番号を指定してアクセスしていると、リーダーが切り替わった際にエラーになるでしょう。何らかのプロキシが接続をまとめて受ける形にしておく必要があります。

または個々のプログラムはローカルにあるrqliteを更新する形にすれば、自動的にデータの同期を行ってくれます。なお、この場合は各プログラミング言語向けのライブラリが必要になると思われます(現在はGo、C/C++、node.js、Pythonくらいのようです)。

その他のオプション

rqliteでは認証にパスワードを用いたり、SSL証明書を適用してHTTPSアクセスする設定も行えます。また、ホットバックアップにも対応しています。


SQLiteをクラスタリングしようという考えはユニークです。手軽に使えるのが魅力ではあるのですが、その使い勝手の良さをクラスタリングによってプロダクションレベルまで引き上げようとしています。

rqliteが参考に挙げているのはetcdやConsulといったスケールするKVSのリレーショナルデータベース版です。ぜひ触れてみてください。

rqlite/rqlite: The lightweight, distributed relational database