redis_fdw による PostgreSQL と Redis の連携

redis_fdw は、リレーショナルデータベース PostgreSQL からキーバリューストア Redis にアクセスする拡張モジュールです。redis_fdw を使うことで、高速なデータの読み書きができる Redis と、SQL で複雑なデータの操作ができる PostgreSQL 間で円滑なデータの連携ができます。

この記事では、redis_fdw の概要、インストール、基本的な使い方、テーブルオプションについて説明します。なお、Redis 自体の説明については Redis の紹介 をお読みください。

redis_fdw とは

redis_fdw の名前につく fdw は外部データラッパ (Foreign Data Wrapper) を表し、PostgreSQL から Oracle や CSV ファイルなど、PostgreSQL 外のデータに外部テーブルとしてアクセスする機能です。redis_fdw はこの機能を使って Redis 上のデータにアクセスします。

redis_fdw は、もともと PostgreSQL 開発者の一人 Dave Page 氏が実験的に開発し、その後、Andrew Dunstan 氏が開発を引き継ぎ、本番利用向けに改修しました。

ソースコードは GitHub リポジトリ上で公開され、PostgreSQL ライセンスに従って配布されています。PostgreSQL の対応バージョンは 9.1 以降で、2022 年 8 月時点で最新の PostgreSQL 14 にも対応しています。

なお、PGDG リポジトリ[1] で同じ名前のパッケージが配布されていますが、ここで取り上げる redis_fdw とは異なるもので、便宜上、リポジトリ名をとって rw_redis_fdw と呼びます。

rw_redis_fdw も Redis 用外部データラッパで、名前につく rw は読み書き (Read-Write) を表し、以前は書き込みに対応していなかった redis_fdw に対して名づけられたものと考えられますが、現在は redis_fdw も書き込みに対応しています。

rw_redis_fdw は redis_fdw に比べて機能は多いですが、ここでは、より広く使われている redis_fdw を取り上げます。

redis_fdw のインストール

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

ここでは、Rocky Linux 8 上に PostgreSQL、Redis をセットアップした上、redis_fdw をソースコードからビルドし、インストールします。

  1. PGDG リポジトリをインストールします。

    $ sudo dnf -qy install https://download.postgresql.org/pub/repos/yum/reporpms/EL-$(rpm -E %rhel-%_arch)/pgdg-redhat-repo-latest.noarch.rpm
    
    Installed:
      pgdg-redhat-repo-42.0-24.noarch
    
    

    PostgreSQL は PGDG リポジトリからインストールします。

    これ以降、dnf コマンドの実行時に署名つきパッケージの検証に必要な PGP キーの取り込みを表すメッセージが出力される場合があります。

  2. PostgreSQL モジュールを無効にします。

    $ sudo dnf -qy module disable postgresql
    

    OS 標準のリポジトリからも PostgreSQL をインストールできますが、バージョンが古いので、PGDG リポジトリのほうを使うように、PostgreSQL モジュールを無効にしています。

  3. PostgreSQL をインストールします。

    $ sudo dnf -qy install postgresql14-server
    
    Installed:
      lz4-1.8.3-3.el8_4.x86_64
      postgresql14-14.4-1PGDG.rhel8.x86_64
      postgresql14-libs-14.4-1PGDG.rhel8.x86_64
      postgresql14-server-14.4-1PGDG.rhel8.x86_64
    
    

    ここでは、PostgreSQL 14 をインストールしています。

    パッケージ名 postgresql14-server をはじめ、これ以降、コマンドラインに含まれる 14 は PostgreSQL のバージョンを表します。お使いになるバージョンに合わせ、適宜、読み替えてください。

  4. データベースクラスタを作成します。

    $ PGSETUP_INITDB_OPTIONS="-E UTF8 --no-locale" sudo -E /usr/pgsql-14/bin/postgresql-14-setup initdb
    Initializing database ... OK
    
    

    データベースクラスタはデータベースを格納する領域です。作成には通常 initdb コマンドを使いますが、ここでは、パッケージに付属のスクリプトを使います。

    環境変数 PGSETUP_INITDB_OPTIONS には initdb コマンドに渡すオプションを指定します。ここでは、デフォルトのエンコーディングを UTF8、ロケールをなしに指定しています。

  5. PostgreSQL のサービスを起動します。

    $ sudo systemctl start postgresql-14
    
  6. Redis をインストールします。

    $ sudo dnf -qy install redis
    
    Installed:
      redis-5.0.3-5.module+el8.5.0+657+2674830e.x86_64
    
    
  7. Redis のサービスを起動します。

    $ sudo systemctl start redis
    
  8. EPEL リポジトリ[2] をインストールします。

    $ sudo dnf -qy install epel-release
    
    Installed:
      epel-release-8-13.el8.noarch
    
    

    redis_fdw の動作に必要な Redis 用 C 言語ライブラリ Hiredis は EPEL リポジトリからインストールします。

  9. redis_fdw のビルドに必要なパッケージをインストールします。

    $ sudo dnf -qy install make redhat-rpm-config postgresql14-devel hiredis-devel
    
    Upgraded:
      libgcc-8.5.0-10.1.el8_6.x86_64          libgomp-8.5.0-10.1.el8_6.x86_64
      libstdc++-8.5.0-10.1.el8_6.x86_64
    Installed:
      annobin-10.29-3.el8.x86_64
      binutils-2.30-113.el8.x86_64
      clang-13.0.1-1.module+el8.6.0+825+7e27476a.x86_64
      clang-devel-13.0.1-1.module+el8.6.0+825+7e27476a.x86_64
      clang-libs-13.0.1-1.module+el8.6.0+825+7e27476a.x86_64
      clang-resource-filesystem-13.0.1-2.module+el8.6.0+987+d36ea6a1.x86_64
      clang-tools-extra-13.0.1-1.module+el8.6.0+825+7e27476a.x86_64
      cmake-filesystem-3.20.2-4.el8.x86_64
      compiler-rt-13.0.1-1.module+el8.6.0+825+7e27476a.x86_64
      cpp-8.5.0-10.1.el8_6.x86_64
      dwz-0.12-10.el8.x86_64
      efi-srpm-macros-3-3.el8.noarch
      emacs-filesystem-1:26.1-7.el8.noarch
      gcc-8.5.0-10.1.el8_6.x86_64
      gcc-c++-8.5.0-10.1.el8_6.x86_64
      ghc-srpm-macros-1.4.2-7.el8.noarch
      glibc-devel-2.28-189.5.el8_6.x86_64
      glibc-headers-2.28-189.5.el8_6.x86_64
      go-srpm-macros-2-17.el8.noarch
      hiredis-0.13.3-13.el8.x86_64
      hiredis-devel-0.13.3-13.el8.x86_64
      isl-0.16.1-6.el8.x86_64
      kernel-headers-4.18.0-372.13.1.el8_6.x86_64
      libicu-devel-60.3-2.el8_1.x86_64
      libmpc-1.1.0-9.1.el8.x86_64
      libomp-13.0.1-1.module+el8.6.0+825+7e27476a.x86_64
      libomp-devel-13.0.1-1.module+el8.6.0+825+7e27476a.x86_64
      libstdc++-devel-8.5.0-10.1.el8_6.x86_64
      libxcrypt-devel-4.1.1-6.el8.x86_64
      llvm-13.0.1-1.module+el8.6.0+825+7e27476a.x86_64
      llvm-devel-13.0.1-1.module+el8.6.0+825+7e27476a.x86_64
      llvm-libs-13.0.1-1.module+el8.6.0+825+7e27476a.x86_64
      llvm-static-13.0.1-1.module+el8.6.0+825+7e27476a.x86_64
      llvm-test-13.0.1-1.module+el8.6.0+825+7e27476a.x86_64
      make-1:4.2.1-11.el8.x86_64
      ocaml-srpm-macros-5-4.el8.noarch
      openblas-srpm-macros-2-2.el8.noarch
      perl-srpm-macros-1-25.el8.noarch
      postgresql14-devel-14.4-1PGDG.rhel8.x86_64
      python-rpm-macros-3-41.el8.noarch
      python-srpm-macros-3-41.el8.noarch
      python3-rpm-macros-3-41.el8.noarch
      qt5-srpm-macros-5.15.2-1.el8.0.1.noarch
      redhat-rpm-config-129-1.el8.noarch
      rust-srpm-macros-5-2.el8.noarch
      unzip-6.0-46.el8.x86_64
      zip-3.0-23.el8.x86_64
    
    
  10. redis_fdw のソースコードをダウンロードします。

    $ curl -LOs https://github.com/pg-redis-fdw/redis_fdw/archive/refs/heads/REL_14_STABLE.tar.gz
    

    redis_fdw のソースコードは PostgreSQL のバージョンごとにブランチが分かれていて、ダウンロード時はブランチ名を指定する必要があります。ここでは、PostgreSQL 14 に対応したブランチ REL_14_STABLE を指定しています。

  11. redis_fdw のソースコードを展開します。

    $ tar -xzf REL_14_STABLE.tar.gz
    
  12. redis_fdw をビルドします。

    $ make -C redis_fdw-REL_14_STABLE USE_PGXS=1 PG_CONFIG=/usr/pgsql-14/bin/pg_config
    make: ディレクトリ '/home/vagrant/redis_fdw-REL_14_STABLE' に入ります
    gcc -Wall -Wmissing-prototypes -Wpointer-arith -Wdeclaration-after-statement -Werror=vla -Wendif-labels -Wmissing-format-attribute -Wimplicit-fallthrough=3 -Wcast-function-type -Wformat-security -fno-strict-aliasing -fwrapv -fexcess-precision=standard -Wno-format-truncation -Wno-stringop-truncation -O2 -g -pipe -Wall -Werror=format-security -Wp,-D_FORTIFY_SOURCE=2 -Wp,-D_GLIBCXX_ASSERTIONS -fexceptions -fstack-protector-strong -grecord-gcc-switches -specs=/usr/lib/rpm/redhat/redhat-hardened-cc1 -specs=/usr/lib/rpm/redhat/redhat-annobin-cc1 -m64 -mtune=generic -fasynchronous-unwind-tables -fstack-clash-protection -fcf-protection -fPIC -I. -I./ -I/usr/pgsql-14/include/server -I/usr/pgsql-14/include/internal  -D_GNU_SOURCE -I/usr/include/libxml2  -I/usr/include  -c -o redis_fdw.o redis_fdw.c
    gcc -Wall -Wmissing-prototypes -Wpointer-arith -Wdeclaration-after-statement -Werror=vla -Wendif-labels -Wmissing-format-attribute -Wimplicit-fallthrough=3 -Wcast-function-type -Wformat-security -fno-strict-aliasing -fwrapv -fexcess-precision=standard -Wno-format-truncation -Wno-stringop-truncation -O2 -g -pipe -Wall -Werror=format-security -Wp,-D_FORTIFY_SOURCE=2 -Wp,-D_GLIBCXX_ASSERTIONS -fexceptions -fstack-protector-strong -grecord-gcc-switches -specs=/usr/lib/rpm/redhat/redhat-hardened-cc1 -specs=/usr/lib/rpm/redhat/redhat-annobin-cc1 -m64 -mtune=generic -fasynchronous-unwind-tables -fstack-clash-protection -fcf-protection -fPIC -shared -o redis_fdw.so redis_fdw.o -L/usr/pgsql-14/lib  -Wl,--as-needed -L/usr/lib64  -L/usr/lib64 -Wl,--as-needed -Wl,-rpath,'/usr/pgsql-14/lib',--enable-new-dtags  -lhiredis
    /usr/bin/clang -Wno-ignored-attributes -fno-strict-aliasing -fwrapv -Wno-unused-command-line-argument -Wno-compound-token-split-by-macro -O2  -I. -I./ -I/usr/pgsql-14/include/server -I/usr/pgsql-14/include/internal  -D_GNU_SOURCE -I/usr/include/libxml2  -I/usr/include -flto=thin -emit-llvm -c -o redis_fdw.bc redis_fdw.c
    make: ディレクトリ '/home/vagrant/redis_fdw-REL_14_STABLE' から出ます
    

    引数の変数 USE_PGXS には PostgreSQL 用拡張モジュールの構築基盤 PGXS を使うことを表す 1PG_CONFIG には pg_config コマンドのパスを指定します。

  13. redis_fdw をインストールします。

    $ sudo make -C redis_fdw-REL_14_STABLE install USE_PGXS=1 PG_CONFIG=/usr/pgsql-14/bin/pg_config
    make: ディレクトリ '/home/vagrant/redis_fdw-REL_14_STABLE' に入ります
    /usr/bin/mkdir -p '/usr/pgsql-14/lib'
    /usr/bin/mkdir -p '/usr/pgsql-14/share/extension'
    /usr/bin/mkdir -p '/usr/pgsql-14/share/extension'
    /usr/bin/install -c -m 755  redis_fdw.so '/usr/pgsql-14/lib/redis_fdw.so'
    /usr/bin/install -c -m 644 .//redis_fdw.control '/usr/pgsql-14/share/extension/'
    /usr/bin/install -c -m 644 .//redis_fdw--1.0.sql  '/usr/pgsql-14/share/extension/'
    /usr/bin/mkdir -p '/usr/pgsql-14/lib/bitcode/redis_fdw'
    /usr/bin/mkdir -p '/usr/pgsql-14/lib/bitcode'/redis_fdw/
    /usr/bin/install -c -m 644 redis_fdw.bc '/usr/pgsql-14/lib/bitcode'/redis_fdw/./
    cd '/usr/pgsql-14/lib/bitcode' && /usr/bin/llvm-lto -thinlto -thinlto-action=thinlink -o redis_fdw.index.bc redis_fdw/redis_fdw.bc
    make: ディレクトリ '/home/vagrant/redis_fdw-REL_14_STABLE' から出ます
    

    ソースコードと、それを展開したディレクトリはインストール後、削除しても構いません。

redis_fdw の基本的な使い方

redis_fdw を使って PostgreSQL から Redis 上のデータを操作してみます。

  1. PostgreSQL のスーパーユーザに切り替わります。

    $ sudo su - postgres
    
  2. データベースを作成します。

    $ createdb redis_db
    

    ここでは、redis_db という名前のデータベースを作成しています。

  3. データベースに接続します。

    $ psql redis_db
    psql (14.4)
    "help"でヘルプを表示します。
    
    =# 
    

    ここでは、作成した redis_db データベースに接続しています。終了するには \q または Ctrl + D を入力します。

  4. データベースに redis_fdw をインストールします。

    =# CREATE EXTENSION redis_fdw;
    CREATE EXTENSION
    

    redis_fdw はプログラムとしてはインストール済みですが、実際に使うにはデータベースごとにインストールする必要があります。

    ここでは、接続中の redis_db データベースにインストールしています。

  5. 外部サーバを作成します。

    =# CREATE SERVER redis_server
         FOREIGN DATA WRAPPER redis_fdw
         OPTIONS (address '127.0.0.1', port '6379');
    CREATE SERVER
    

    外部サーバとは、外部データラッパを使って外部データにアクセスするのに必要な接続情報を定義したオブジェクトです。作成時には、外部サーバ名、外部データラッパ名、オプションを指定します。

    redis_fdw では、以下のオプションが指定できます。

    オプション デフォルト値 説明
    address 127.0.0.1 Redis サーバの IP アドレスまたはホスト名
    port 6379 Redis サーバのポート番号

    ここでは、redis_server という名前で redis_fdw の外部サーバを作成しています。オプションは例示のため、指定していますが、デフォルトでよければ、指定する必要はありません。

  6. 外部サーバのユーザマッピングを作成します。

    =# CREATE USER MAPPING FOR PUBLIC
         SERVER redis_server
         OPTIONS (password 'secret');
    CREATE USER MAPPING
    

    外部サーバのユーザマッピングとは、外部サーバとユーザの対応づけを定義したオブジェクトです。作成時には、ユーザ名、外部サーバ名、オプションを指定します。

    redis_fdw では、以下のオプションが指定できます。

    オプション デフォルト値 説明
    password (なし) Redis サーバ接続時の認証に使うパスワード

    ここでは、すべてのユーザを表す PUBLIC と外部サーバ redis_server を対応づけるユーザマッピングを作成しています。password オプションはパスワードが未設定であれば、指定する必要はありません。

  7. 外部テーブルを作成します。

    =# CREATE FOREIGN TABLE string_table (key text, val text)
         SERVER redis_server
         OPTIONS (database '0');
    CREATE FOREIGN TABLE
    

    外部テーブルとは、外部データラッパを使って外部データにテーブルとしてアクセスできるように定義した特別なテーブルです。作成時には、テーブル定義、外部サーバ名、オプションを指定します。

    redis_fdw では、以下のオプションが指定できます。

    オプション デフォルト値 説明
    database 0 Redis のデータベース番号。0 からデフォルトでは 15 まで指定可能
    tabletype (なし) Redis のデータ型。listhashsetzset が指定可能。未指定は string
    tablekeyprefix (なし) 指定した接頭辞をもつキーに対象データを絞り込む
    tablekeyset (なし) 指定したキーをもつ集合に含まれるキーに対象データを絞り込む
    singleton_key (なし) 指定したキーの単一データを展開して対象データにする

    tabletype オプションを指定しないと、文字列を表す string 型になりますが、値として string は指定できません。teblekeyprefixtablekeysetsingleton_key はいずれも対象データを指定するオプションで、1 つのみ指定でき、指定しないと、すべてのデータが対象になります。詳しくは redis_fdw のテーブルオプション で説明します。

    ここでは、tabletype オプションは指定せず、テーブル定義は text 型の keyval 列を定義しています。Redis のキーと string 型の値は PostgreSQL ではそれぞれ text 型に対応します。列名は任意です。database オプションはデフォルトでよければ、指定する必要はありません。

  8. 外部テーブルのデータを操作します。

    データの操作を受けて Redis サーバがどう動作するかを確認するため、Redis サーバで実行されたコマンドを出力するようにしておきます。終了するには Ctrl + C を入力します。

    $ redis-cli monitor
    OK
    

    PostgreSQL 側でデータを 3 件登録します。

    =# INSERT INTO string_table
         VALUES ('key1', 'val1'), ('key2', 'val2'), ('key3', 'val3');
    INSERT 0 3
    

    Redis 側で実行されたコマンドが出力されます。

    1658092725.885750 [0 127.0.0.1:46042] "AUTH" "secret"
    1658092725.886005 [0 127.0.0.1:46042] "SELECT" "0"
    1658092725.886333 [0 127.0.0.1:46042] "EXISTS" "key1"
    1658092725.886766 [0 127.0.0.1:46042] "SET" "key1" "val1"
    1658092725.887016 [0 127.0.0.1:46042] "EXISTS" "key2"
    1658092725.887387 [0 127.0.0.1:46042] "SET" "key2" "val2"
    1658092725.887620 [0 127.0.0.1:46042] "EXISTS" "key3"
    1658092725.887764 [0 127.0.0.1:46042] "SET" "key3" "val3"
    

    PostgreSQL 側でデータを 1 件更新します。

    =# UPDATE string_table SET val = 'val4' WHERE key = 'key1';
    UPDATE 1
    

    Redis 側で実行されたコマンドが出力されます。

    1658092937.940434 [0 127.0.0.1:46044] "AUTH" "secret"
    1658092937.940542 [0 127.0.0.1:46044] "SELECT" "0"
    1658092937.940660 [0 127.0.0.1:46044] "DBSIZE"
    1658092937.941136 [0 127.0.0.1:46046] "AUTH" "secret"
    1658092937.941352 [0 127.0.0.1:46046] "SELECT" "0"
    1658092937.941445 [0 127.0.0.1:46046] "EXISTS" "key1"
    1658092937.941835 [0 127.0.0.1:46048] "AUTH" "secret"
    1658092937.941976 [0 127.0.0.1:46048] "SELECT" "0"
    1658092937.942118 [0 127.0.0.1:46046] "GET" "key1"
    1658092937.942385 [0 127.0.0.1:46048] "SET" "key1" "val4"
    

    PostgreSQL 側でデータを 1 件削除します。

    =# DELETE FROM string_table WHERE key = 'key2';
    DELETE 1
    

    Redis 側で実行されたコマンドが出力されます。

    1658092947.811658 [0 127.0.0.1:46050] "AUTH" "secret"
    1658092947.811792 [0 127.0.0.1:46050] "SELECT" "0"
    1658092947.811875 [0 127.0.0.1:46050] "DBSIZE"
    1658092947.812375 [0 127.0.0.1:46052] "AUTH" "secret"
    1658092947.812605 [0 127.0.0.1:46052] "SELECT" "0"
    1658092947.812729 [0 127.0.0.1:46052] "EXISTS" "key2"
    1658092947.813055 [0 127.0.0.1:46054] "AUTH" "secret"
    1658092947.813197 [0 127.0.0.1:46054] "SELECT" "0"
    1658092947.813342 [0 127.0.0.1:46052] "GET" "key2"
    1658092947.813438 [0 127.0.0.1:46054] "DEL" "key2"
    

    PostgreSQL 側でデータを 1 件取得します。

    =# SELECT val FROM string_table WHERE key = 'key3';
     val
    ------
     val3
    (1 行)
    
    

    Redis 側で実行されたコマンドが出力されます。

    1658092954.987736 [0 127.0.0.1:46056] "AUTH" "secret"
    1658092954.987827 [0 127.0.0.1:46056] "SELECT" "0"
    1658092954.987876 [0 127.0.0.1:46056] "DBSIZE"
    1658092954.988113 [0 127.0.0.1:46058] "AUTH" "secret"
    1658092954.988283 [0 127.0.0.1:46058] "SELECT" "0"
    1658092954.988375 [0 127.0.0.1:46058] "EXISTS" "key3"
    1658092954.988482 [0 127.0.0.1:46058] "GET" "key3"
    

redis_fdw のテーブルオプション

redis_fdw を使って外部テーブルの定義時に指定できるオプションについて説明します。

tabletype オプション

tabletype は Redis のデータ型を指定するオプションです。オプションに指定できるデータ型について説明します。

list

list は登録順に並べた文字列の集合を表すデータ型で、PostgreSQL では値を並べた text 型の配列に対応します。

=# CREATE FOREIGN TABLE list_table (key text, val text[])
     SERVER redis_server
     OPTIONS (database '1', tabletype 'list');
CREATE FOREIGN TABLE
=# INSERT INTO list_table
     VALUES ('key1', '{val1,val2,val3,val1}');
INSERT 0 1
=# SELECT * FROM list_table WHERE key = 'key1';
 key  |          val
------+-----------------------
 key1 | {val1,val2,val3,val1}
(1 行)

=# SELECT key, unnest(val) FROM list_table WHERE key = 'key1';
 key  | unnest
------+--------
 key1 | val1
 key1 | val2
 key1 | val3
 key1 | val1
(4 行)

val1val2val3val1 を登録し、データを取得すると、値が登録順に並び、重複する値 val1 が残っています。取得したデータは unnest 関数で行の集合に展開できます。

hash

hash はフィールドと値の文字列の組み合わせからなる集合を表すデータ型で、PostgreSQL ではフィールドと値を交互に並べた text 型の配列に対応します。

=# CREATE FOREIGN TABLE hash_table (key text, val text[])
     SERVER redis_server
     OPTIONS (database '2', tabletype 'hash');
CREATE FOREIGN TABLE
=# INSERT INTO hash_table
     VALUES ('key1', '{fld1,val1,fld2,val2,fld3,val3,fld1,val4}');
INSERT 0 1
=# SELECT * FROM hash_table WHERE key = 'key1';
 key  |               val
------+---------------------------------
 key1 | {fld1,val4,fld2,val2,fld3,val3}
(1 行)

=# SELECT key, json_object(val) FROM hash_table WHERE key = 'key1';
 key  |                     json_object
------+-----------------------------------------------------
 key1 | {"fld1" : "val4", "fld2" : "val2", "fld3" : "val3"}
(1 行)

=# SELECT key, (json_each_text(json_object(val))).*
     FROM hash_table WHERE key = 'key1';
 key  | key  | value
------+------+-------
 key1 | fld1 | val4
 key1 | fld2 | val2
 key1 | fld3 | val3
(3 行)

フィールドと値の組み合わせ fld1val1fld2val2fld3val3fld1val4 を登録し、データを取得すると、フィールド fld1 が重複する値 val1val4 はあとから登録した値 val4 で上書きされています。取得したデータは json_object 関数で JSON オブジェクトに変換し、さらに json_each_text 関数でフィールドと値からなる行の集合に展開できます。

set

set は順序づけがなく、重複がない文字列の集合を表すデータ型で、PostgreSQL では値を並べた text 型の配列に対応します。

=# CREATE FOREIGN TABLE set_table (key text, val text[])
     SERVER redis_server
     OPTIONS (database '3', tabletype 'set');
CREATE FOREIGN TABLE
=# INSERT INTO set_table
     VALUES ('key1', '{val1,val2,val3,val1}');
INSERT 0 1
=# SELECT * FROM set_table WHERE key = 'key1';
 key  |       val
------+------------------
 key1 | {val1,val3,val2}
(1 行)

val1val2val3val1 を登録し、データを取得すると、重複する値 val1 が取り除かれ、1 つになっています。

zset

zset はスコアで順序づけした、重複がない文字列の集合を表すデータ型で、PostgreSQL では値を並べた text 型の配列に対応します。

zset 型の使い方は基本的に set 型と同じです。スコアは登録順に 012、… が割り当てられます。スコアを PostgreSQL で扱うには、singleton_key オプション を指定する必要があります。

tablekeyprefix オプション

tablekeyprefix は指定した接頭辞をもつキーに対象データを絞り込むオプションで、1 つのデータベース内に異なる種類のデータをもたせ、別テーブルとしてアクセスするのに役立ちます。

=# CREATE FOREIGN TABLE keyprefix_table (key text, val text)
     SERVER redis_server
     OPTIONS (database '4', tablekeyprefix 'keyprefix_table:');
CREATE FOREIGN TABLE
=# INSERT INTO keyprefix_table VALUES ('keyprefix_table:key1', 'val1');
INSERT 0 1
=# INSERT INTO keyprefix_table VALUES ('keyprefix_table:key2', 'val2');
INSERT 0 1
=# INSERT INTO keyprefix_table VALUES ('key3', 'val3');
ERROR:  key 'key3' does not match table key prefix 'keyprefix_table:'
=# SELECT * FROM keyprefix_table;
     key          | val
----------------------+------
 keyprefix_table:key2 | val2
 keyprefix_table:key1 | val1
(2 行)

=# \! redis-cli -n 4 SET key3 val3
OK
=# \! redis-cli -n 4 KEYS \*
1) "keyprefix_table:key2"
2) "key3"
3) "keyprefix_table:key1"
=# SELECT * FROM keyprefix_table;
     key          | val
----------------------+------
 keyprefix_table:key2 | val2
 keyprefix_table:key1 | val1
(2 行)

tablekeyprefix オプションを指定すると、指定した接頭辞 keyprefix_table: をもつキー keyprefix_table:key1keyprefix_table:key2 のデータしか登録できず、接頭辞をもたないキー key3 の登録はエラーになります。\! は PostgreSQL 外のコマンドを実行するコマンドで、Redis 上のデータを直接操作し、接頭辞をもたないキー key3 を登録し、データを取得すると、接頭辞をもたないキー key3 は結果に現れません。

大量のキーをもつデータベースでは、tablekeyprefix オプションではキーの検索に時間がかかるため、tablekeyset オプションでキーを集合に含まれるものに限ったほうがよいでしょう。

tablekeyset オプション

tablekeyset は指定したキーをもつ集合に含まれるキーに対象データを絞り込むオプションで、tablekeyprefix オプションと同じく、1 つのデータベース内で異なる種類のデータを扱うのに使います。

=# CREATE FOREIGN TABLE keyset_table (key text, val text)
     SERVER redis_server
     OPTIONS (database '5', tablekeyset 'keyset_table_keys');
CREATE FOREIGN TABLE
=# INSERT INTO keyset_table
     VALUES ('key1', 'val1'), ('key2', 'val2'), ('key3', 'val3');
INSERT 0 3
=# SELECT * FROM keyset_table;
 key  | val
------+------
 key3 | val3
 key2 | val2
 key1 | val1
(3 行)

=# \! redis-cli -n 5 SREM keyset_table_keys key2
(integer) 1
=# \! redis-cli -n 5 SMEMBERS keyset_table_keys
1) "key3"
2) "key1"
=# SELECT * FROM keyset_table;
 key  | val
------+------
 key3 | val3
 key1 | val1
(2 行)

tablekeyset オプションを指定すると、指定したキー keyset_table_keys をもつ集合に登録したキー key1key2key3 が自動的に登録され、取得されるデータは集合に含まれるキーに限られます。Redis 上のデータを直接操作し、キー key2 を削除し、データを取得すると、削除したキー key2 は結果に現れなくなります。

singleton_key オプション

singleton_key は指定したキーの単一データを展開して対象データにするオプションで、tableytpe オプション指定なしの string 型は 1 行 1 列、指定ありの list または set 型は行の集合、hash 型はフィールドと値からなる行の集合、zset 型は値とスコアからなる行の集合に展開されます。

=# CREATE FOREIGN TABLE singleton_table (val text, sc numeric)
     SERVER redis_server
     OPTIONS (database '6', tabletype 'zset', singleton_key 'singleton_table');
CREATE FOREIGN TABLE
=# INSERT INTO singleton_table
     VALUES ('val1', 10), ('val2', 20), ('val3', 30);
INSERT 0 3
=# SELECT * FROM singleton_table;
 val  | sc
------+----
 val1 | 10
 val2 | 20
 val3 | 30
(3 行)

=# UPDATE singleton_table SET sc = 40 WHERE val = 'val1';
UPDATE 1
=# SELECT * FROM singleton_table;
 val  | sc
------+----
 val2 | 20
 val3 | 30
 val1 | 40
(3 行)

singleton_key オプションと併せて tabletype オプションに zset 型を指定すると、PostgreSQL でスコアを扱えるようになります。スコアは PostgreSQL では numeric 型に対応します。ORDER BY 句で並び順を指定しなければ、データはスコアの昇順に取得されます。値 val1 のスコアを 10 から 40 に更新すると、並び順が更新したスコア順に変わり、値 val1 は最後に来るようになります。

おわりに

この記事では、Redis 用外部データラッパ redis_fdw の概要、インストール、基本的な使い方、テーブルオプションについて説明しました。redis_fdw を使うことで、高速なデータの読み書きができる Redis と、SQL で複雑なデータの操作ができる PostgreSQL の特長を互いに活かし、さらに利用の幅が広がることでしょう。

SRA OSS では、PostgreSQL や Redis、redis_fdw のサポートをはじめとして、オープンソースに関する様々なサービスを提供しています。SRA OSS のサービスに興味がありましたら、お気軽に こちら からお問い合わせください。


  1. PostgreSQL Global Development Group (PGDG) が運営する PostgreSQL 関連パッケージの Yum リポジトリ
  2. Fedora Project が運営するエンタープライズ Linux 向け追加パッケージ (Extra Packages for Enterprise Linux; EPEL) の Yum リポジトリ