[PostgreSQL][Redis]redis_fdwを使ってみた

postgresql大量のユーザリクエストを Redis に書き込み、foreign data wrapper(FDW) を使って定期的に PostgreSQL にロードするユースケースが PgCon 2013 で発表されていた。

Using PostgreSQL with Redis
Using native wrappers and a Foreign Data Wrapper for two-way Redis integration
http://www.pgcon.org/2013/schedule/events/574.en.html

フロントエンドの Redis では高速にリクエストを処理しつつ、バックエンドの PostgreSQL ではデータウェアハウス的な処理を行うのが狙い。

トークでは

の2つの Redis 向け FDW が紹介されている。

面白そうだったので redis_fdw の方を触ってみた。

foreign data wrapper(FDW) とは

外部データを SQL で操作する SQL/MED(MED は Management of External Data のこと)という規格が ISO/IEC 9075-9:2003 で定儀されている。この PostgreSQL 実装が FDW で 9.1 から導入されている。

以下の流れで PostgreSQL から外部データを操作する

  1. CREATE SERVER で外部データにアクセスするためのサーバーを定儀
  2. CREATE USER MAPPING で PostgreSQL ユーザと上で定義したサーバとのユーザのマッピングを行う
  3. CREATE FOREIGN TABLE で外部データを SQL 操作するためのテーブルを作成
  4. SQL で FOREIGN TABLE を操作

FDW の概要は PostgreSQL コア開発者 Andrew Dunstan さんが次のスライドにまとめている

PostgreSQL Foreign Data Wrappers
http://people.planetpostgresql.org/andrew/uploads/fdw2.pdf

Install

Install PostgreSQL

今回インストールする redis_fdw は git の master が PostreSQL 9.3 向けなので、master を試したい人は事前に 9.3 をインストールする必要がある。

Install hiredis

Redis の C Client である Hiredis をインストール

$ git  clone https://github.com/redis/hiredis.git
Cloning into 'hiredis'...
remote: Counting objects: 2078, done.
remote: Compressing objects: 100% (895/895), done.
remote: Total 2078 (delta 1230), reused 1961 (delta 1134)
Receiving objects: 100% (2078/2078), 653.21 KiB | 426 KiB/s, done.
Resolving deltas: 100% (1230/1230), done.

$ cd hiredis/
$ make
$ sudo make install
mkdir -p /usr/local/include/hiredis /usr/local/lib
cp -a hiredis.h async.h adapters /usr/local/include/hiredis
cp -a libhiredis.so /usr/local/lib/libhiredis.so.0.10
cd /usr/local/lib && ln -sf libhiredis.so.0.10 libhiredis.so.0
cd /usr/local/lib && ln -sf libhiredis.so.0 libhiredis.so
cp -a libhiredis.a /usr/local/lib
$ sudo /sbin/ldconfig
$ sudo ldconfig -p | grep hiredis
        libhiredis.so.0.10 (libc6,x86-64) => /usr/local/lib/libhiredis.so.0.10
        libhiredis.so (libc6,x86-64) => /usr/local/lib/libhiredis.so

Install redis_fdw

9.3 がインストールされていない状態で、 redis_fdw の master に対してコンパイルすると

redis_fdw.c:25:2: error: #error wrong Postgresql version this branch is only for 9.3
make: *** [redis_fdw.o] Error 1

というようなエラーメッセージが表示される。
ローカルの環境に合わせてブランチを変えること

$ git clone https://github.com/pg-redis-fdw/redis_fdw.git
$ cd redis_fdw/
$ git branch -r
  origin/HEAD -> origin/master
  origin/REL9_1_STABLE
  origin/REL9_2_STABLE
  origin/master
$ PATH=/usr/lib/postgresql/9.2/bin/:$PATH make USE_PGXS=1
gcc -g -O2 -fstack-protector --param=ssp-buffer-size=4 -Wformat -Wformat-security -Werror=format-security -fPIC -pie -I/usr/include/mit-krb5 -DLINUX_OOM_ADJ=0 -Wall -Wmissing-prototypes -Wpointer-arith -Wdeclaration-after-statement -Wendif-labels -Wmissing-format-attribute -Wformat-security -fno-strict-aliasing -fwrapv -fexcess-precision=standard -g -fpic -I. -I. -I/usr/include/postgresql/9.2/server -I/usr/include/postgresql/internal -D_FORTIFY_SOURCE=2 -D_GNU_SOURCE -I/usr/include/libxml2  -I/usr/include/tcl8.5  -c -o redis_fdw.o redis_fdw.c
redis_fdw.c:25:2: error: #error wrong Postgresql version this branch is only for 9.3
make: *** [redis_fdw.o] Error 1

$ git checkout -b REL9_2_STABLE origin/REL9_2_STABLE
Branch REL9_2_STABLE set up to track remote branch REL9_2_STABLE from origin.
Switched to a new branch 'REL9_2_STABLE'

$ sudo PATH=/usr/lib/postgresql/9.2/bin/:$PATH make USE_PGXS=1 install
/bin/mkdir -p '/usr/lib/postgresql/9.2/lib'
/bin/mkdir -p '/usr/share/postgresql/9.2/extension'
/bin/mkdir -p '/usr/share/postgresql/9.2/extension'
/bin/sh /usr/lib/postgresql/9.2/lib/pgxs/src/makefiles/../../config/install-sh -c -m 755  redis_fdw.so '/usr/lib/postgresql/9.2/lib/redis_fdw.so'
/bin/sh /usr/lib/postgresql/9.2/lib/pgxs/src/makefiles/../../config/install-sh -c -m 644 ./redis_fdw.control '/usr/share/postgresql/9.2/extension/'
/bin/sh /usr/lib/postgresql/9.2/lib/pgxs/src/makefiles/../../config/install-sh -c -m 644 ./redis_fdw--1.0.sql  '/usr/share/postgresql/9.2/extension/'

CREATE EXTENSION

$ psql
psql (9.2.4)
Type "help" for help.

# create extension hstore;
CREATE EXTENSION
postgres=# \dx
                                       List of installed extensions
     Name      | Version |   Schema   |                            Description
---------------+---------+------------+-------------------------------------------------------------------
 fuzzystrmatch | 1.0     | public     | determine similarities and distance between strings
 hstore        | 1.1     | public     | data type for storing sets of (key, value) pairs
 pg_trgm       | 1.0     | public     | text similarity measurement and index searching based on trigrams
 plpgsql       | 1.0     | pg_catalog | PL/pgSQL procedural language
 redis_fdw     | 1.0     | public     | Foreign data wrapper for querying a Redis server
(4 rows)

エクステンション redis_fdw がインストールされている。

Usage

次のテストコード を例に、redis_fdw の使い方を確認

https://github.com/pg-redis-fdw/redis_fdw/tree/master/test/sql

Quick Start

まずは Redis の String 型を FDW で連携してみる。

Redis の db#15 に String 型データを投入

> select 15
OK
> set foo bar
OK
> set baz blurfl
OK

CREATE SERVER で外部データにアクセスするためのサーバーを定儀

create server localredis foreign data wrapper redis_fdw;

CREATE USER MAPPING で PostgreSQL ユーザと上で定義したサーバとのユーザのマッピングを行う

create user mapping for public server localredis;

CREATE FOREIGN TABLE で外部データを SQL 操作するためのテーブルを作成
options でデータベース番号を指定。

create foreign table db15(key text, value text)
       server localredis
       options (database '15');

PostgreSQL から SELECT する

# \d db15
       Foreign table "public.db15"
 Column | Type | Modifiers | FDW Options
--------+------+-----------+-------------
 key    | text |           |
 value  | text |           |
Server: localredis
FDW Options: (database '15')

# select * from db15;
 key | value
-----+--------
 baz | blurfl
 foo | bar
(2 rows)

MONITOR コマンドで Redis に投げられているコマンドを確認すると、以下のようになっている。

"SELECT" "15"
"DBSIZE"
"SELECT" "15"
"KEYS" "*"
"GET" "baz"
"GET" "foo"

KEYS でデータベース#15 のキー一覧を取得し、 STRING 型であることを期待して GET コマンドで VALUE を取得している。
db15 に STRING 型以外のデータがあった場合、それらデータは SELECT 結果から無視される。

型を指定

FDW は String 型以外にも対応している。foreign table を CREATE するときに、tabletype でデータ型を指定すればよい。

create foreign table db15h(key text, value text)
       server localredis
       options (database '15', tabletype 'hash');

tabletype には hash, set, zset, list のなかから指定可能。tabletype を指定しなかった場合は string 型とみなされる。
明示的に tabletype ‘string’ というようにすると

ERROR:  invalid tabletype (string) - must be hash, list, set or zset

というようなエラーが発生する謎な仕様なので注意が必要。

Redis の db#15 に Hash 型データを投入

> hmset hash1 k1 v1 k2 v2 k3 v3 k4 v4
OK
> hmset hash2 k1 v5 k2 v6 k3 v7 k4 v8
OK

PostgreSQL から SELECT する

# select * from db15h;
  key  |                   value
-------+-------------------------------------------
 hash1 | {"k1","v1","k2","v2","k3","v3","k4","v4"}
 hash2 | {"k1","v5","k2","v6","k3","v7","k4","v8"}
(2 rows)

MONITOR コマンドで Redis に投げられているコマンドを確認すると、以下のようになっている。

"KEYS" "*"
"HGETALL" "hash1"
"HGETALL" "hash2"
"HGETALL" "baz"
"HGETALL" "foo"

VALUE の取得には、先ほどの GET のかわりに、Hash 型ということで HGETALL が使われている。

キーの Prefix を指定

データベース番号とデータ型が同じでも、キーによって保存内容は大きく異なる。
foreign table を CREATE するときに、tablekeyprefix でキーの prefix を指定すると、キーを prefix で絞れる。

create foreign table db15hp(key text, value text)
       server localredis
       options (tabletype 'hash', tablekeyprefix 'hash', database '15');

Redis の db#15 に別の Hash 型データを投入

> hmset h1 k1 v1 k2 v2 k3 v3 k4 v4
OK
> hmset h2 k1 v5 k2 v6 k3 v7 k4 v8
OK

PostgreSQL から SELECT する

# select * from db15h;
  key  |                   value
-------+-------------------------------------------
 hash1 | {"k1","v1","k2","v2","k3","v3","k4","v4"}
 hash2 | {"k1","v5","k2","v6","k3","v7","k4","v8"}
 h1    | {"k1","v1","k2","v2","k3","v3","k4","v4"}
 h2    | {"k1","v5","k2","v6","k3","v7","k4","v8"}
(4 rows)
# select * from db15hp;
  key  |                   value
-------+-------------------------------------------
 hash1 | {"k1","v1","k2","v2","k3","v3","k4","v4"}
 hash2 | {"k1","v5","k2","v6","k3","v7","k4","v8"}
(2 rows)

db15hp ではキーが hash で始まるものに絞られている。

MONITOR コマンドで Redis に投げられているコマンドを確認すると、以下のようになっている。

"KEYS" "hash*"
"HGETALL" "hash1"
"HGETALL" "hash2"

KEYS コマンドに tablekeyprefix を利用している。

hstore と連携

Hash なのにマッピングを表現できていないのは悲しいので、hstore で k/v として扱えるように、value を array で定儀する(value text[])。

create foreign table db15hpa(key text, value text[])
       server localredis
       options (tabletype 'hash', tablekeyprefix 'hash', database '15');

hstore と組み合わせることで、Redis の Hash 型を PostgreSQL でもいかせる

# select key, value from db15hpa;
  key  |           value
-------+---------------------------
 hash1 | {k1,v1,k2,v2,k3,v3,k4,v4}
 hash2 | {k1,v5,k2,v6,k3,v7,k4,v8}
(2 rows)

# select key, hstore(value) from db15hpa;
  key  |                     hstore
-------+------------------------------------------------
 hash1 | "k1"=>"v1", "k2"=>"v2", "k3"=>"v3", "k4"=>"v4"
 hash2 | "k1"=>"v5", "k2"=>"v6", "k3"=>"v7", "k4"=>"v8"
(2 rows)

エクステンション hstore がインストールされていない場合は、以下のコマンドを実行

# create extension hstore;
CREATE EXTENSION

singleton key

tablekeyprefix をもう少し推し進めて、PostgreSQL のテーブルでは Redis の特定のキーのデータだけを扱いたいときがある。

foreign table を CREATE するときに、singleton_key でキー を指定すればよい。

http://adpgtech.blogspot.jp/2013/05/redis-fdw-singleton-key-tables.html

create foreign table db15gh(key text, value text)
       server localredis
       options (tabletype 'hash', singleton_key 'hash1', database '15');
# select * from db15gh;
 key | value
-----+-------
 k1  | v1
 k2  | v2
 k3  | v3
 k4  | v4
(4 rows)

MONITOR コマンドで Redis に投げられているコマンドを確認すると、以下のようになっている。

"SELECT" "15"
"HLEN" "hash1"
"SELECT" "15"
"HGETALL" "hash1"

KEYS コマンドでキー一覧の取得はせず、 singleton_key で指定したキーを直接取得している。

FDW の更新処理

PostgreSQL の FDW は 9.3 から参照だけでなく更新も行えるようになる。
9.3 標準でついている postgresql fdw では更新に対応しているが、redis_fdw は現時点では未対応。

#2:Add support for writing tables
https://github.com/pg-redis-fdw/redis_fdw/issues/2

9.3 の postgres_fdw の使い方は SRA さんの評価レポートを参照のこと

PostgreSQL 9.3 beta1 検証レポート
http://www.sraoss.co.jp/technology/postgresql/images/20130517_PostgreSQL9.3_report_sraoss.pdf

FDW で書き込み出来ると、バックエンドの PostgreSQL で管理しているマスタデータを、フロントエンドの Redis に即時反映できるようになるので、使い勝手がかなり良くなる。

性能評価

TODO

References

Tagged with: , , ,
Posted in database

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Archives
  • RT @__apf__: How to write a research paper: a guide for software engineers & practitioners. docs.google.com/presentation/d… /cc @inwyrd 1 week ago
  • RT @HayatoChiba: 昔、自然と対話しながら数学に打ち込んだら何かを悟れるのではと思いたち、専門書1つだけ持ってパワースポットで名高い奈良の山奥に1週間籠ったことがある。しかし泊まった民宿にドカベンが全巻揃っていたため、水島新司と対話しただけで1週間過ぎた。 それ… 3 weeks ago
  • RT @googlecloud: Ever wonder what underwater fiber optic internet cables look like? Look no further than this deep dive w/ @NatAndLo: https… 3 weeks ago
  • @ijin UTC+01:00 な時間帯で生活しています、、、 6 months ago
  • RT @mattcutts: Google's world-class Site Reliability Engineering team wrote a new book: amazon.com/Site-Reliabili… It's about managing produc… 9 months ago
%d bloggers like this: