株式会社クリアコード > ククログ

ククログ


公式のWindows版PostgreSQLパッケージ用の拡張機能のビルドシステムの作り方

PostgreSQLは各種プラットフォーム用のパッケージを提供しているため、簡単にインストールすることができます。PostgreSQL用の拡張機能のインストール方法も簡単です。拡張機能のバイナリーと設定ファイル*1を拡張機能用のディレクトリーに配置してCREATE EXTENSIONを実行するだけです。

GNU MakeがあるシステムではPGXS*2という拡張機能のビルドを支援する仕組みを使うことができます。PGXSを使えば数十行程度でGCCを使って拡張機能をビルドする仕組みができます*3

しかし、公式のWindows版PostgreSQLパッケージ*4はVisual Studioでビルドされているため、GCCを使うPGXSでは公式のWindows版PostgreSQLパッケージ用の拡張機能をビルドできません。そこで、CMakeを使って公式のWindows版PostgreSQLパッケージ用のビルドシステムの作り方を紹介します。

なお、MinGWを使ってWindows版PostgreSQLをビルドする場合はPGXSを使えます*5。また、PostgreSQL本体と一緒に拡張機能をビルドする場合はPostgreSQL自体のビルドシステムを使えます*6。ここで紹介する方法は「公式のWindows版PostgreSQLパッケージ用に拡張機能だけをビルドする」ビルドシステムの作り方です。

公式のWindows版PostgreSQLパッケージ用拡張機能のビルド方法

ビルドシステムを作るためにはビルド方法を確立していなければいけません。Visual Studio用の拡張機能のビルド方法はPostgreSQLのWikiにまとまっています。ざっくり言うと次の設定をしたVisual Studioのソリューションファイルを作ってビルドします。

  • DLLを作成する
  • 素のCとしてコンパイルする
  • C++の例外を無効にする
  • includeの検索パスに↓を追加する
    • include\server\port\win32_msvc
    • include\server\port\win32
    • include\server
    • include
  • ライブラリーの検索パスに↓を追加する
    • lib
  • postgres.libをリンクする

Wikiではこれらの設定をしたプロジェクトファイルを手動で作る前提になっているように見えますが、バージョン管理システムと相性が悪いですし、更新にはVisual Studioが必要になりメンテナンスしづらいのでやりたくありません。ということで、これらの設定をしたVisual Studioでのビルド用のファイルを生成することにします*7

CMakeを使った公式のWindows版PostgreSQLパッケージ用拡張機能のビルドシステムの使い方

CMakeを使うとVisual Studioでのビルド用のファイルを生成することができます。また、CMakeはCPackというパッケージ作成用の仕組みも用意しているのでビルド済みの拡張機能のパッケージ作成までできます。

CMakeを使った場合は次のようにすればビルドできます。「%PostgreSQLをインストールしたフォルダー%」と「%PostgreSQLのバージョン%」は適切な値に置き換えます。例えば、「..\pgsql」と「9.4.1-3」といった具合です。

> cmake . -G "Visual Studio 12 2013 Win64" -DCMAKE_INSTALL_PREFIX=%PostgreSQLをインストールしたフォルダー% -DPOSTGRESQL_VERSION=%PostgreSQLのバージョン%
> cmake --build . --config Release

ビルドした拡張機能をインストールする場合は次のようにします。

> cmake --build . --config Release --target install

パッケージを作成する場合は次のようにします。

> cmake --build . --config Release --target package

簡単ですね。

CMakeを使った公式のWindows版PostgreSQLパッケージ用拡張機能のビルドシステム

ではCMake用の設定ファイル(CMakeLists.txt)を作ります。ベースは次のようになります。ここに前述のVisual Studioでの設定を追加していきます。

cmake_minimum_required(VERSION 3.0.0)

set(PROJECT_NAME "拡張機能名") # 例: "PGroonga"
set(PROJECT_ID "拡張機能のID") # 例: "pgroonga"

set(VENDOR "拡張機能の開発者") # 例: "The PGroonga Project"

set(VERSION_MAJOR "拡張機能のメジャーバージョン") # 例: "0"
set(VERSION_MINOR "拡張機能のマイナーバージョン") # 例: "5"
set(VERSION_PATCH "拡張機能のパッチバージョン")   # 例: "0"
# ↑は.controlから抽出することもできる
# 方法は後述するPGroongaの例を参照すること
set(VERSION_FULL "${VERSION_MAJOR}.${VERSION_MINOR}.${VERSION_PATCH}")

project("${PROJECT_ID}")

# ↓はユーザーが指定した「%PostgreSQLをインストールしたフォルダー%」になる
set(POSTGRESQL_DIR "${CMAKE_INSTALL_PREFIX}"
  CACHE PATH "PostgreSQL binary directory")
# ↓はパッケージを作成するときだけ必要
set(POSTGRESQL_VERSION "unknown"
  CACHE STRING "PostgreSQL version")

set(LIBRARY_NAME "lib${PROJECT_ID}")

set(EXTENSION_DIR "lib")
set(EXTENSION_DATA_DIR "share/extension")
set(DOCUMENT_DIR "share/${PROJECT_ID}")

set(SOURCES
  "ソースコード1.c"
  "ソースコード2.c"
  "...")

Visual Studioでの設定を再確認します。

  • DLLを作成する
  • 素のCとしてコンパイルする
  • C++の例外を無効にする
  • includeの検索パスに↓を追加する
    • include\server\port\win32_msvc
    • include\server\port\win32
    • include\server
    • include
  • ライブラリーの検索パスに↓を追加する
    • lib
  • postgres.libをリンクする

順に設定します。まずは「DLLを作成する」です。SHAREDとファイル名のベース名を拡張機能IDにすることがポイントです。

add_library("${LIBRARY_NAME}" SHARED ${SOURCES})
set_target_properties("${LIBRARY_NAME}"
   PROPERTIES
   OUTPUT_NAME "${PROJECT_ID}")

次は「素のCとしてコンパイルする」ですが、これは特になにもする必要はありません。

続いて「C++の例外を無効にする」は/EHscオプションを指定します。詳細はMSDNの/EH(例外処理モデル)のドキュメントを参考にしてください。

set_source_files_properties(${SOURCES}
  PROPERTIES
  COMPILE_FLAGS "/EHsc")

「includeの検索パスを追加する」は次のようにします。

include_directories(
  "${POSTGRESQL_DIR}/include/server/port/win32_msvc"
  "${POSTGRESQL_DIR}/include/server/port/win32"
  "${POSTGRESQL_DIR}/include/server"
  "${POSTGRESQL_DIR}/include")

「ライブラリーの検索パスを追加する」は次のようにします。

link_directories(
  "${POSTGRESQL_DIR}/lib")

postgres.libをリンクする」は次のようにします。

target_link_libraries("${LIBRARY_NAME}"
  "postgres.lib")

これで必要な設定が完了しました。簡単ですね。後はインストール先の指定とパッケージ作成用の設定だけです。

インストール先の指定は次のようになります。

install(TARGETS "${LIBRARY_NAME}"
  DESTINATION "${EXTENSION_DIR}")

install(FILES
  "${PROJECT_SOURCE_DIR}/${PROJECT_ID}.control"
  DESTINATION "${EXTENSION_DATA_DIR}")

install(FILES
  "${PROJECT_SOURCE_DIR}/${PROJECT_ID}.sql"
  RENAME "${PGRN_PROJECT_ID}--${VERSION_FULL}.sql"
  DESTINATION "${PGRN_EXTENSION_DATA_DIR}")

パッケージ作成用の設定は次の通りです。すでに設定した値を使っているだけです。

set(CPACK_GENERATOR "ZIP")
set(CPACK_INCLUDE_TOPLEVEL_DIRECTORY OFF)
set(CPACK_PACKAGE_VERSION_MAJOR "${VERSION_MAJOR}")
set(CPACK_PACKAGE_VERSION_MINOR "${VERSION_MINOR}")
set(CPACK_PACKAGE_VERSION_PATCH "${VERSION_PATCH}")
set(CPACK_PACKAGE_VENDOR "${VENDOR}")
if(CMAKE_CL_64)
  set(PACKAGE_SYSTEM_NAME "x64")
else()
  set(PACKAGE_SYSTEM_NAME "x86")
endif()
set(CPACK_PACKAGE_FILE_NAME
  "${PROJECT_ID}-${VERSION_FULL}-postgresql-${POSTGRESQL_VERSION}-${PACKAGE_SYSTEM_NAME}")

include(CPack)

まとめると次の通りです。

cmake_minimum_required(VERSION 3.0.0)

set(PROJECT_NAME "拡張機能名") # 例: "PGroonga"
set(PROJECT_ID "拡張機能のID") # 例: "pgroonga"

set(VENDOR "拡張機能の開発者") # 例: "The PGroonga Project"

set(VERSION_MAJOR "拡張機能のメジャーバージョン") # 例: "0"
set(VERSION_MINOR "拡張機能のマイナーバージョン") # 例: "5"
set(VERSION_PATCH "拡張機能のパッチバージョン")   # 例: "0"
# ↑は.controlから抽出することもできる
# 方法は後述するPGroongaの例を参照すること
set(VERSION_FULL "${VERSION_MAJOR}.${VERSION_MINOR}.${VERSION_PATCH}")

project("${PROJECT_ID}")

# ↓はユーザーが指定した「%PostgreSQLをインストールしたフォルダー%」になる
set(POSTGRESQL_DIR "${CMAKE_INSTALL_PREFIX}"
  CACHE PATH "PostgreSQL binary directory")
# ↓はパッケージを作成するときだけ必要
set(POSTGRESQL_VERSION "unknown"
  CACHE STRING "PostgreSQL version")

set(LIBRARY_NAME "lib${PROJECT_ID}")

set(EXTENSION_DIR "lib")
set(EXTENSION_DATA_DIR "share/extension")
set(DOCUMENT_DIR "share/${PROJECT_ID}")

set(SOURCES
  "ソースコード1.c"
  "ソースコード2.c"
  "...")

add_library("${LIBRARY_NAME}" SHARED ${SOURCES})
set_target_properties("${LIBRARY_NAME}"
   PROPERTIES
   OUTPUT_NAME "${PROJECT_ID}")

set_source_files_properties(${SOURCES}
  PROPERTIES
  COMPILE_FLAGS "/EHsc")

include_directories(
  "${POSTGRESQL_DIR}/include/server/port/win32_msvc"
  "${POSTGRESQL_DIR}/include/server/port/win32"
  "${POSTGRESQL_DIR}/include/server"
  "${POSTGRESQL_DIR}/include")

link_directories(
  "${POSTGRESQL_DIR}/lib")

target_link_libraries("${LIBRARY_NAME}"
  "postgres.lib")

install(TARGETS "${LIBRARY_NAME}"
  DESTINATION "${EXTENSION_DIR}")

install(FILES
  "${PROJECT_SOURCE_DIR}/${PROJECT_ID}.control"
  DESTINATION "${EXTENSION_DATA_DIR}")

install(FILES
  "${PROJECT_SOURCE_DIR}/${PROJECT_ID}.sql"
  RENAME "${PGRN_PROJECT_ID}--${VERSION_FULL}.sql"
  DESTINATION "${PGRN_EXTENSION_DATA_DIR}")

set(CPACK_GENERATOR "ZIP")
set(CPACK_INCLUDE_TOPLEVEL_DIRECTORY OFF)
set(CPACK_PACKAGE_VERSION_MAJOR "${VERSION_MAJOR}")
set(CPACK_PACKAGE_VERSION_MINOR "${VERSION_MINOR}")
set(CPACK_PACKAGE_VERSION_PATCH "${VERSION_PATCH}")
set(CPACK_PACKAGE_VENDOR "${VENDOR}")
if(CMAKE_CL_64)
  set(PACKAGE_SYSTEM_NAME "x64")
else()
  set(PACKAGE_SYSTEM_NAME "x86")
endif()
set(CPACK_PACKAGE_FILE_NAME
  "${PROJECT_ID}-${VERSION_FULL}-postgresql-${POSTGRESQL_VERSION}-${PACKAGE_SYSTEM_NAME}")

include(CPack)

実際に使われているCMakeの設定ファイルはPGroongaのCMakeLists.txtを参考にするとよいでしょう。

また、実際に作成したパッケージはhttp://packages.groonga.org/windows/pgroonga/以下にあります。こちらもあわせて参考にしてください。

まとめ

公式のWindows版PostgreSQLパッケージ用の拡張機能のビルドシステムの作り方を紹介しました。ここで紹介した方法はCMakeを使っています。Visual StudioでビルドしなければいけないときにCMakeは便利なので、そんなときはCMakeを思い出してみてください。

*1 拡張機能のメタデータを書いた.controlファイルと拡張機能のインストール方法を書いた.sqlファイル。

*2 なんの略かわかりませんが、PostgreSQL Extension Systemの略な気はします。

*3 ただし、.deb.rpmなどパッケージを作る仕組みは提供していません。それらは自分で作る必要があります。PGroongaが使っている仕組みが参考になるかもしれません。

*4 EnterpriseDBが提供していますが、これは公式のパッケージなんですよ…ね?

*5 PGXSの中身を見ると使えるように見えます。

*6 ソースを見ると使えるように見えます。

*7 PostgreSQL自体のビルドシステムもこのアプローチのように見えます。

2015-05-11

2015年6月6日(土)に学生向けリーダブルコード勉強会を開催予定

要約:6月6日(土)に学生向けリーダブルコード勉強会を開催する予定なので、興味のある学生の人はイベントページから応募してください。興味のありそうな学生を知っている人は教えてあげてください。締め切りは6月1日(月)です。

去年も開催した学生向けリーダブルコード勉強会を今年も開催します。(去年は参加者多数につき2回開催しました。1回目の開催レポート2回目の開催レポート。)今年も勉強会の講演者・トレーナーをリーダブルコードの解説を書いた須藤が務めます。

リーダブルコードはとても内容が評価されている本です。リーダブルコードが発売されたのは3年前ですが、今でも新しく読む人が増えています。例えば、2014年のジュンク堂書店池袋本店でのコンピュータ書売上冊数ランキングで1位*1になったり、Amazonのクラウドエンジニアが選ぶ技術書35選でも一番最初に紹介されていたりします。学生のうちからこの本に書かれていることを理解し、実践していると、今後、プログラミングをする上で大きな力になるでしょう。

内容

内容は去年と同じくリーダブルコードの解説に書いていることの体験です。解説では、本文に書いているリーダブルコードを書くための方法をどうやって実践するか、その方法を説明しています。つまり、学生がリーダブルコードに書いていることを単なる知識ではなく、実際に自分の技術や考え方として身につけるきっかけとなる内容です。

リーダブルコードを書くには読む人の視点を持っていることが重要です。リーダブルかどうかは読む人の視点での基準だからです。読む人の視点を持たずに書いているうちは、単に「想像して」リーダブルだろうコードを書いているだけです。読む人の視点を持っていれば、「事実に裏付けされた」リーダブルコードを書けます。

しかし、学生で読む人の視点を持っている人は少ないです。仕事では複数の人が同じソフトウェアを開発することが多く、また、リリースしたあとに継続して開発を続けることも多いので、コードを読む機会が増え、読む人の視点が身につきやすいです。一方、学生は自分ひとりで開発し、作って動かして研究成果が得られたらコードに触れることはない、というケースがほとんどです。そうすると、自分が書いたにしろ他の人が書いたにしろ、コードを読む機会がほとんどなく、読む人の視点を身につけることができません。

今回の勉強会ではこの「読む人の視点」の体験を重視します。具体的には次の流れで勉強会を進めることで、「読む人の視点」を体験します。

  1. 参加者全員にこの勉強会で開発するプログラムの仕様を伝える。
    • 参加者全員が同じプログラムを開発する。
    • プログラムの難易度は簡単なものにする*2
  2. 参加者全員が一定時間で各自プログラムを開発する。
    • 一定時間経過後、みんな途中まで開発を進めている状態にする。
  3. 参加者同士で開発しているプログラムを交換し、交換したプログラムを元に開発を継続する。
    • 開発を継続するためには交換したプログラム(= 他の人が書いたコード)を理解する必要がある。
      • 複数人での共同開発や作って終わりではない開発の疑似体験となり、コードを読む機会となる。これで「読む人の視点」を体験する。
      • 自分が書いたコードよりもリーダブルに書いているコードを見つけることを重視してもらう。ダメなところ探しに終始してグチグチ言っていたら勉強会が終わっていた、となったら失敗。
  4. 実際に開発をし、「読む人の視点」を体験してみて感じたことをまとめ、整理する
    • 「面白かった」、「難しかった」だけで終わらせず、「なぜ難しかったか」など理由まで落としこむ。理由まで落としこめれば、今回だけの体験で終わらず自分に身についた今後も活かせる財産になるはず。

「コードを読む」というと「コードレビュー」というイメージが根強いので、どうしても「悪いところはないか?」という視点で読んでしまいがちです。そうではなく、「コードから学ぶ」という視点で読めるようになると、コードの理解も速くなりますし、読んでいる人の成長スピードもあがります。日々新しい技術が生まれ、学ぶことが増えている昨今、コードから学べるようになると重宝されることでしょう。

参加する学生の人には「読む人の視点」、もっと言うと「悪いところはないか?」ではなく「コードから学ぶ」という「読む人の視点」を身につけ、コードを読み、よい書き方を身につけ、それを自分が書くコードにも活かす、という習慣を体験してもらいたいです。

メンター

去年はメンターとして企業で活躍中のトップエンジニアが学生をサポートしました。具体的には、クックパッドのまきもとさん、楽天の川原さん、DeNAのたなべさん、クリアコードの結城さん沖元さんがサポートしました。学生でなくてもサポートして欲しいですよね。

今年は去年よりもたくさんのトップエンジニアが学生をサポートする予定です*3。現在スポンサーになることが決定している企業は次の通りです。調整中の企業もあるのでまだ増える見込みです。

これらの企業からトップエンジニアがメンターとして参加し、学生さんをサポートしてくれます。例えば、クラウドワークスからはCTOの大場さんがメンターとして参加します。他にもメンターが決まったら随時イベントページに載るはずです。

まとめ

6月6日(土)に開催する学生向けリーダブルコード勉強会を紹介しました。リーダブルなコードを書くために大事な「読む人の視点をもつこと」を体験できる勉強会です。興味のある学生の人はイベントページから応募してください。興味のありそうな学生を知っている人はこの勉強会を教えてあげてください。あるいは、成長させたい学生を知っている人はこの勉強会への参加を勧めてください。

なお、締め切りは6月1日(月)です。

*1 2012年、2013年も1位なので、3年連続1位。しかも、2014年はその前の年より売れたみたいです。

*2 難しいプログラムを開発するためのテクニックを学ぶことが目的ではない。

*3 たぶん。

2015-05-13

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はすべてマスターノードで実行します。

データを投入します。

1
2
3
4
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コマンドがあります。');

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

1
2
3
4
5
6
7
8
9
10
11
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を送ればサポートが追加されるかもしれません。

タグ: Groonga
2015-05-18

PostgreSQLで日本語全文検索 - LIKEとpg_bigmとPGroonga

PostgreSQLアンカンファレンス@東京(2015/5/30)でPostgreSQLの日本語全文検索まわりについて紹介しようかとたくらんでいます。しかし、現時点(2015-05-25)でキャンセル待ちで、当日参加できないかもしれないので紹介しようと用意している内容をここにまとめます。

内容

この資料の目的は、PostgreSQLで使える次の3つの方法の特性を紹介し、ユーザーが適切な方法を選択するための材料を提供することです。

LIKE

LIKEのメリット・デメリットは次の通りです。

メリット
  • 標準で使える
  • インデックス作成不要(= データ更新が遅くならない)
  • データが少なければ十分速い
デメリット
  • データ量に比例して遅くなる

ユーザーがLIKEを使うかどうかの判断基準は「十分速いかどうか」(= 「データが少ないかどうか」)です。では、どのくらいなら「十分速い」のでしょうか。

「十分速い」かどうかは「検索対象のデータでの計測結果」と「要件」で判断できます。どちらもケースバイケースですが、参考情報としての計測結果なら示すことができます。

参考情報としてpg_bigmでいろんなデータを日本語検索してみよう!LIKEの結果を使ってみましょう。この記事では次の3つのデータを検索しています。

  • 青空文庫の書籍一覧データ
  • 住所データ
  • 日本版Wikipediaのタイトル一覧データ

それぞれみていきましょう。

まずは「青空文庫の書籍一覧データ」です。

このデータの特性と検索速度は次の通りです。

件数 1レコードあたりのバイト数 検索速度
11,818件 17バイト 6.673ms

6msで結果が返ってくるのであれば多くの要件で「十分速い」と言えるでしょう。つまり、このデータでは日本語全文検索の方法としてLIKEはアリということです。

次のデータは「住所データ」です。

このデータの特性と検索速度は次の通りです。

件数 1レコードあたりのバイト数 検索速度
147,769件 14バイト 70.684ms

70msで結果が返ってくるのであれば多くの要件で「十分速い」と言えるでしょう。つまり、このデータでも日本語全文検索の方法としてLIKEはアリということです。

最後のデータは「日本版Wikipediaのタイトル一覧データ」です。

このデータの特性と検索速度は次の通りです。

件数 1レコードあたりのバイト数 検索速度
2,461,588件 20バイト 943.450ms

1秒くらいで結果が返ってくるのでは、「十分速い」というのは要件次第でしょう。つまり、この規模のデータでは日本語全文検索の方法としてLIKEは使えるかもしれないし使えないかもしれない、といったところです。

これまでの結果をまとめると次の通りです。LIKEで十分かどうかを判断する場合は、実際のデータの傾向と検索時間を材料として検討します。

件数 1レコードあたりのバイト数 検索速度
11,818件 17バイト 6.673ms
147,769件 14バイト 70.684ms
2,461,588件 20バイト 943.450ms

では、LIKEでは十分速くない場合はどうしたらよいでしょうか。次のどちらかの方法を選びます。

  • pg_bigm
  • PGroonga
pg_bigm

pg_bigmのメリット・デメリットは次の通りです。

メリット
  • データ量が多くても高速
  • ストリーミングレプリケーションを使える
デメリット
  • 別途インストールしないといけない
  • インデックス作成が遅い(= データ更新が遅くなる)
  • ヒット数に比例して遅くなる

pg_bigmはデータ量が多くても高速ですが、ヒット数が多いほど遅くなる特性があります。pg_bigmが十分速いかどうかを判断するためにも、LIKEと同様に「検索対象のデータでの計測結果」と「要件」が必要です。ただし、LIKEとは注目するポイントが異なります。

それでは、検索結果例を見ながら注目するポイントを確認しましょう。なお、ここで紹介する検索結果例はPGroongaとpg_bigmのベンチマーク結果 · Issue #2 · groonga/wikipedia-searchにあります。手順もまとまっているので誰でも追試することができます。 ここで使うデータは日本語版Wikipediaの本文です。このデータの特性は次の通りです。

件数 1レコードあたりのバイト数
1,846,514件 3777バイト

注目するポイントの1つ目はインデックス作成時間です。元データのロード時間とインデックス作成時間は次の通りです。

元データのロード時間 インデックス作成時間
16分31秒 5時間56分15秒

約16分でロードできるデータのインデックス作成に約6時間かかるのは十分速いでしょうか。それとも遅いでしょうか。要件と照らしあわせて検討します。

なお、この時間はインデックス全体を1から作成する時間です。常に少量ずつ更新するという要件であればこの時間はあまり関係ありません。そのような要件の場合は、少量のデータの更新が滞らない(更新処理が溜まっていかない)くらいの速度がでていれば十分速いと判断できます。

注目するポイントの2つ目はヒット数と検索時間の関係です。

検索語 ヒット数 検索時間
「PostgreSQL」または「MySQL」 361 0.107s
データベース 17168 1.224s
テレビアニメ 22885 2.472s
日本 625792 0.556s

pg_bigmはヒット数が多くなるほど遅くなります。数百件ヒットのときは0.1秒ですが、1万7千件のときは1秒ちょい、2万2千件のときは約2.5秒となりました。なお、このデータではヒット数が増えるほど遅くなる傾向がよりわかりやすいデータになっています。というのは、このデータは1レコードあたりのデータが大きいからです。pg_bigmは1レコードあたりのデータが大きいと遅くなりやすい特性があります。

なお、検索語が2文字以下の場合はヒット数が多くても遅くなりにくいという特性があります。例では62万件ヒットしても0.5秒で結果を返しています。

このような特性があるpg_bigmなので、データによっては遅いと判断する場面も十分速いと判断する場面もあるでしょう。

PGroonga

LIKEでは遅い場合、pg_bigm以外の選択肢はPGroongaです。

PGroongaのメリット・デメリットは次の通りです。

メリット
  • インデックス作成が速い
  • データ量が多くても高速
  • ヒット数が多くても高速
デメリット
  • 別途インストールしないといけない
  • ストリーミングレプリケーション非対応
  • 使用ディスクサイズが多い

PGroongaはデータ量が多くてもヒット数が多くても速度が落ちにくく、インデックス作成も高速ですが、PostgreSQLとの連携度合いはpg_bigmほどではありません。たとえば、pg_bigmはPostgreSQL標準のストリーミングレプリケーション機能を使えますが、PGroongaは使えません。これはPostgreSQL側が連携する機能を提供していないことが原因です。今後連携できる機能が提供される見込みですが、現時点で連携できないことは事実です。

PGroongaを選ぶかどうかの基準は他の方法では遅いケースがPGroongaだと速いかどうかです。そのためには同じデータでpg_bigmと比較する必要があります。

それでは、pg_bigmと同じデータを使った場合のPGroongaの速度を確認しましょう。

まずはインデックス作成時間です。

元データのロード時間 16分31秒
PGroongaのインデックス作成時間 25分37秒
pg_bigmのインデックス作成時間 5時間56分15秒

参照しやすいようにpg_bigmのインデックス作成時間も再掲しています。

PGroongaは元データのロード時間の2倍いかないくらいの時間でインデックスを作成しています。pg_bigmと比べると1/14の時間でインデックスを構築できています。

ただし、インデックスのために使用するディスクサイズは大きいです。

元データロード直後のデータベースサイズ 4.0GiB
PGroongaの使用ディスクサイズ 14.0GiB
pg_bigmの使用ディスクサイズ 3.6GiB

ディスクサイズが小さい環境で動かす場合は問題になるかもしれません。

次は検索時間を確認しましょう。

検索語 ヒット数 PGroongaの検索時間 pg_bigmの検索時間
「PostgreSQL」または「MySQL」 368 0.030s 0.107s
データベース 17172 0.121s 1.224s
テレビアニメ 22885 0.179s 2.472s
日本 625792 0.646s 0.556s

PGroongaはpg_bigmほどヒット数の多さに影響をうけていません。62万件ヒットする場合でも0.6秒ちょいで結果を返しています。PGroongaは全体的に高速ですが、検索語が2文字以下の場合はpg_bigmの方が高速です。

PostgreSQLとの連携度合いよりも性能が大事な要件な場合で、pg_bigmでは性能が落ちやすい規模のデータではPGroongaが選択肢になるでしょう。

まとめ

PostgreSQLで日本語全文検索の方法として次の3つの方法を紹介しました。

  • LIKE
  • pg_bigm
  • PGroonga

データが少ないならLIKEで十分です。要件次第ですが、1レコード数十バイトの小さなデータなら百万件くらいまでいけるかもしれません。

データが多いならpg_bigmかPGroongaを選びます。

次のような場合はpg_bigmがよいでしょう。

  • 2文字以下での検索がほとんど
  • 1レコードあたりのデータが小さい
  • ヒット数が多くなることがほとんどない
  • ストリーミングレプリケーションを使いたい

pg_bigmで次のような問題があるならPGroongaがよいでしょう。

  • 1レコードあたりのデータが大きい
  • ヒット数が多くなるケースを考慮しないといけない
  • 更新が多くてインデックス更新速度がネックになる

ちなみに、PGroongaでもレプリケーションをする方法はあります。興味のある方はpg_shardとPGroongaを使ったレプリケーション対応の高速日本語全文検索可能なPostgreSQLクラスターの作り方を参考にしてください。

最後に、PostgreSQLの日本語全文検索に興味のある方にお願いです。この説明の中のpg_bigmとPGroongaの性能確認で参照したベンチマークをあなたの環境でも動かして結果を教えてもらえないでしょうか?同じ傾向があるか確認したいのです。ベンチマークの実行方法や必要な環境などの詳細は協力者募集:PGroongaとpg_bigmのベンチマークを実行を参照してください。

タグ: Groonga
2015-05-25

«前月 最新記事 翌月»
タグ:
年・日ごとに見る
2008|05|06|07|08|09|10|11|12|
2009|01|02|03|04|05|06|07|08|09|10|11|12|
2010|01|02|03|04|05|06|07|08|09|10|11|12|
2011|01|02|03|04|05|06|07|08|09|10|11|12|
2012|01|02|03|04|05|06|07|08|09|10|11|12|
2013|01|02|03|04|05|06|07|08|09|10|11|12|
2014|01|02|03|04|05|06|07|08|09|10|11|12|
2015|01|02|03|04|05|06|07|08|09|10|11|12|
2016|01|02|03|04|05|06|07|08|09|10|11|12|
2017|01|02|03|04|05|06|07|08|09|10|11|12|
2018|01|02|03|04|05|06|07|08|09|10|11|12|
2019|01|02|03|04|05|06|07|08|09|10|11|12|
2020|01|02|03|04|05|06|07|08|09|10|11|12|