pg_shardとPGroongaを使ったレプリケーション対応の高速日本語全文検索可能なPostgreSQLクラスターの作り方 - 2015-05-18 - ククログ

ククログ

株式会社クリアコード > ククログ > pg_shardとPGroongaを使ったレプリケーション対応の高速日本語全文検索可能なPostgreSQLクラスターの作り方

pg_shardとPGroongaを使ったレプリケーション対応の高速日本語全文検索可能なPostgreSQLクラスターの作り方

PostgreSQL 9.4.1の標準機能で日本語全文検索をする場合、LIKEを使うしかありません。LIKEには次の問題があります。

  • レコード数に比例して遅くなる。
  • 日本語文字列の正規化機能がない1

日本語文字列の正規化機能には、例えば、半角カタカナ2と通常のカタカナの違いを吸収する機能があります。違いを吸収すると、「パパ」(通常のカタカナ)で検索すると「パパ」(半角カタカナ)もマッチします。

正規化し過ぎると誤検出が増える可能性が増えるのでなんでも正規化すればよいというものではありません。しかし、半角カタカナと通常のカタカナの同一視のように多くの場合で有用な正規化はあります。

標準機能でなければ日本語全文検索には次の選択肢があります。

pg_bigmは高速に検索できますが、ヒット数が多いとき・検索対象の文字列が長いときに性能が落ちやすいです3。また、日本語文字列の正規化機能はありません。

PGroongaはGroonga(ぐるんが)という全文検索エンジンをバックエンドに使っているため、高速に検索できますし日本語文字列の正規化機能もあります。また、bigram以外にもtrigramやMeCabを使ったトークナイズ方式を選べたり、pg_bigmよりも更新性能が高いという特徴があります4。しかし、PostgreSQLのレプリケーション機能を使えないという欠点があります。

日本語全文検索機能だけで考えるとPGroongaの方が充実していますが、可用性という面ではpg_bigmの方が便利です。それは、pg_bigmはPostgreSQL標準のストリーミングレプリケーション機能を使ってレプリケーションできるからです。PGroongaはPostgreSQL標準のストリーミングレプリケーション機能を使えないため5、サーバーが落ちたときにバックアップサーバーにすぐに切り替える、ということができません6

そこで、pg_shardを使ったPGroongaのレプリケーションシステムの作り方を紹介します。PGroongaを使いたいけどレプリケーションがネックなんだよな。。。という人は参考にしてください。

pg_shard

pg_shardはシャーディング機能(大きなデータを複数のノードに分散)とレプリケーション機能(同じデータを複数のノードでコピー)を提供する拡張機能です。

開発しているのはCitus Dataで、PostgreSQL用のカラム指向のデータストアであるcstore_fdwも開発しています。

pg_shardのレプリケーション機能を使うことでPostgreSQL標準のストリーミングレプリケーションと同等のことを実現できます。

構築方法

pg_shardとPGroongaを使ったレプリケーション対応の高速日本語全文検索可能なPostgreSQLクラスターの構築方法を説明します。CentOS 6向けです。

クラスターには次のノードがあるとします。

役割 ホスト名 IPアドレス
マスターノード master1 192.168.1.67
ワーカーノード1 worker1 192.168.1.68
ワーカーノード2 worker2 192.168.1.69

マスターノードが落ちた時のために、マスターノードはPostgreSQL標準のレプリケーション機能を使ってバックアップサーバーを構築しますが、それはPostgreSQLの標準的な方法なのでここでは省略します。

それでは、まずはワーカーノードをセットアップし、その後マスターノードをセットアップします。

ワーカーノードのセットアップ

ワーカーノードのセットアップ方法を説明します。ワーカーノードは2つありますが、どちらも同じ手順です。

PostgreSQLをパッケージでインストールします。

% sudo -H rpm -ivh http://yum.postgresql.org/9.4/redhat/rhel-$(rpm -qf --queryformat="%{VERSION}" /etc/redhat-release)-$(rpm -qf --queryformat="%{ARCH}" /etc/redhat-release)/pgdg-centos94-9.4-1.noarch.rpm
% sudo -H yum install -y postgresql94-server
% sudo -H /sbin/service postgresql-9.4 initdb
% sudo -H /sbin/chkconfig postgresql-9.4 on

外部からの接続を受けつけるようにします。

postgresql.confではlisten_address*を設定します7

/var/lib/pgsql/9.4/data/postgresql.conf:

listen_addresses = '*'

pg_hba.confでは192.168.1.0/24からの接続はすべて受けつけるようにします。そのため、このネットワークは信頼できるノードだけがいる状態にしてください8

/var/lib/pgsql/9.4/data/pg_hba.conf:

host    all             all             192.168.1.0/24            trust

PGroongaをパッケージでインストールします。

% sudo -H rpm -ivh http://packages.groonga.org/centos/groonga-release-1.1.0-1.noarch.rpm
% sudo -H yum install -y postgresql94-pgroonga

pg_shardはビルドします。

% wget https://github.com/citusdata/pg_shard/archive/v1.1.0.tar.gz
% tar xvf v1.1.0
% cd pg_shard-1.1.0
% sudo -H yum install -y postgresql94-devel gcc
% PATH=/usr/pgsql-9.4/bin:$PATH make
% sudo -H PATH=/usr/pgsql-9.4/bin:$PATH make install

pg_shard用の設定をします。

postgresql.confではshared_preload_librariespg_shardを設定します。

/var/lib/pgsql/9.4/data/postgresql.conf:

shared_preload_libraries = 'pg_shard'           # (change requires restart)

PostgreSQLを起動します。

% sudo -H /sbin/service postgresql-9.4 start

データベースを作成します。今回はpgroonga_replicationという名前のデータベースにします。

% sudo -u postgres -H createdb pgroonga_replication

PGroongaをインストールします。

% sudo -u postgres -H psql pgroonga_replication --command 'CREATE EXTENSION pgroonga'

これでワーカーノードのセットアップは完了です。CREATE EXTENSION pg_shardはする必要はありません。自動で実行されます。

マスターノードのセットアップ

マスターノードのセットアップ方法を説明します。途中まではワーカーノードのセットアップ方法と同じです。

PostgreSQLをパッケージでインストールします。

% sudo -H rpm -ivh http://yum.postgresql.org/9.4/redhat/rhel-$(rpm -qf --queryformat="%{VERSION}" /etc/redhat-release)-$(rpm -qf --queryformat="%{ARCH}" /etc/redhat-release)/pgdg-centos94-9.4-1.noarch.rpm
% sudo -H yum install -y postgresql94-server
% sudo -H /sbin/service postgresql-9.4 initdb
% sudo -H /sbin/chkconfig postgresql-9.4 on

PGroongaをパッケージでインストールします。

% sudo -H rpm -ivh http://packages.groonga.org/centos/groonga-release-1.1.0-1.noarch.rpm
% sudo -H yum install -y postgresql94-pgroonga

pg_shardはビルドします。

% wget https://github.com/citusdata/pg_shard/archive/v1.1.0.tar.gz
% tar xvf v1.1.0
% cd pg_shard-1.1.0
% sudo -H yum install -y postgresql94-devel gcc
% PATH=/usr/pgsql-9.4/bin:$PATH make
% sudo -H PATH=/usr/pgsql-9.4/bin:$PATH make install

pg_shard用の設定をします。

postgresql.confではshared_preload_librariespg_shardを設定します。

/var/lib/pgsql/9.4/data/postgresql.conf:

shared_preload_libraries = 'pg_shard'           # (change requires restart)

ここまではワーカーノードのセットアップ手順と同じです。ここからがマスターノード固有の手順です。

pg_worker_list.confにはワーカーの情報を設定します。今はワーカーは192.168.1.68192.168.1.69で動いている想定なので次のようにします。

/var/lib/pgsql/9.4/data/pg_worker_list.conf:

192.168.1.68 5432
192.168.1.69 5432

PostgreSQLを起動します。

% sudo -H /sbin/service postgresql-9.4 start

ここまではアプリケーションに関係ない共通の設定です。これからはアプリケーション毎の設定です。

ここではサンプルテーブル・データとしてPGroongaのドキュメントに載っている例を用います。

まず、データベースを作成し、PGroongaを初期化します。

% cd /tmp
% sudo -u postgres -H createdb pgroonga_replication
% sudo -u postgres -H psql pgroonga_replication
pgroonga_replication=# CREATE EXTENSION pgroonga;

テーブルとインデックスを定義します。

pgroonga_replication=# CREATE TABLE memos (
  id integer PRIMARY KEY,
  content text
);
pgroonga_replication=# CREATE INDEX pgroonga_content_index ON memos USING pgroonga (content);

データを投入する前にpg_shardの設定をします。

まずpg_shardをインストールします。

pgroonga_replication=# CREATE EXTENSION pg_shard;

次にデータを分散するために使うキーを指定します。今回はプライマリーキーを指定しています。今回はレプリケーション機能だけ使うのであまり意味はありません。

pgroonga_replication=# SELECT master_create_distributed_table('memos', 'id');

続いてシャード数とレプリケーション数を指定します。今回はレプリケーション機能だけ使うので、シャード数1(データ分散なし)、レプリケーション数2にします。

pgroonga_replication=# SELECT master_create_worker_shards('memos', 1, 2);

これでマスターノードの設定は完了です。

動作確認

ワーカーノードとマスターノードの設定が完了したので動作を確認します。

SQLはマスターノードに対してのみ実行します。ワーカーノードは直接触りません。以降のSQLはすべてマスターノードで実行します。

データを投入します。

INSERT INTO memos VALUES (1, 'PostgreSQLはリレーショナル・データベース管理システムです。');
INSERT INTO memos VALUES (2, 'Groongaは日本語対応の高速な全文検索エンジンです。');
INSERT INTO memos VALUES (3, 'PGroongaはインデックスとしてGroongaを使うためのPostgreSQLの拡張機能です。');
INSERT INTO memos VALUES (4, 'groongaコマンドがあります。');

日本語全文検索をします。%%@@も動きます。

pgroonga_replication=# SELECT * FROM memos WHERE content %% '全文検索';
#  id |                      content                      
# ----+---------------------------------------------------
#   2 | Groongaは日本語対応の高速な全文検索エンジンです。
# (1 )
pgroonga_replication=# SELECT * FROM memos WHERE content @@ 'PGroonga OR PostgreSQL';
#  id |                                  content                                  
# ----+---------------------------------------------------------------------------
#   3 | PGroongaはインデックスとしてGroongaを使うためのPostgreSQLの拡張機能です。
#   1 | PostgreSQLはリレーショナル・データベース管理システムです。
# (2 )

データ確認

念のため、ワーカーノードにデータが入っているか直接Groongaを使って確認します。

以下はそれぞれのワーカーノードで実行して確認しています。

groongaコマンドをインストールします。

% sudo -H yum install -y groonga

PGroongaで作ったデータベースをダンプします。

% database_oid=$(sudo -u postgres -H psql --command "SELECT datid FROM pg_stat_database WHERE datname = 'pgroonga_replication'" | head -3 | tail -1 | sed -e 's/ *//g')
% sudo -u postgres -H groonga /var/lib/pgsql/9.4/data/base/${database_oid}/pgrn dump --dump_schema no --dump_indexes no
load --table Sources16552
[
["_key","content"],
[1,"PostgreSQLはリレーショナル・データベース管理システムです。"],
[2,"Groongaは日本語対応の高速な全文検索エンジンです。"],
[3,"PGroongaはインデックスとしてGroongaを使うためのPostgreSQLの拡張機能です。"],
[4,"groongaコマンドがあります。"]
]

データが入っています。もう1つのワーカーノードで実行してもデータが入っていることを確認できます。

PostgreSQL標準のストリーミングレプリケーションのようにレプリケーションできていることが確認できました。

まとめ

PGroongaはPostgreSQLで使える高速な日本語全文検索機能を提供しますが、レプリケーションができないことがネックです。ここではpg_shardと組み合わせることでPGroongaでもレプリケーションする方法を紹介しました。

pg_shardにはALTER TABLEができない、複数のシャードにまたがるトランザクションをサポートしていない、などいくつか制限があります。詳細はpg_shardのREADMEpg_shardのFAQを確認してください。

  1. LIKEの問題というかPostgreSQLのcollation機能がそのような機能を提供していないからです。

  2. Unicodeの文字の名前でいうとHALFWIDTHとついているもの。

  3. Recheck機能のため。

  4. 性能についてはPostgreSQLの日本語対応全文検索モジュールpg_bigmとPGroongaを検証してみた - CreateField Blogを参考にしてください。

  5. PostgreSQLの制限のため。

  6. データはレプリケーションされているので、そのデータのPGroonga用のインデックスを作成するとサービスを提供できるようになります。バックアップサーバーに切り替えるまでに「PGroonga用のインデックスを構築する時間」が必要になります。

  7. 192.168.1.68のように内部用のIPアドレスの方がベターです。

  8. pg_shardはパスワードでの認証をサポートしていません。Pull Requestを送ればサポートが追加されるかもしれません。