PostgreSQL12 beta2も出たし、そろそろPluggable Table Storage Interface（でzheap）を触ってみよう。

# 1. はじめに
リリースノートを見ている方はご存知かと思いますが、ついにPluggable Storage IFが実装されますね。
おもしろそうだし、今後知っておく必要があるとは思っていたので、PG12正式版のリリースが近づく中、そろそろ見始めようと思います。

- 本資料でわかること
  - Pluggable Storage Interfaceの概要
  - 現時点版のzheapのセットアップ及び利用手順
  - pgbenchによるheap vs zheapの比較結果

本資料はせいぜいチュートリアルレベルで、機能の詳細には踏み込んでいません。試しに触ってみようと思う方向けの情報となっていますのでご注意ください。

# 1.1. Pluggable Table Storage Interfaceってなんだっけ？

DBのユースケースは様々あるため、PostgreSQLが現在実装しているストレージの設計では必ずしも最適ではないケースが存在します。
そこで、ストレージとの間を抽象化（これがPluggable Table Storage Interface）して、ユースケースに応じたものを選択できるようにしようという仕組み。（だと理解しています。）

MySQLでストレージエンジンを選択できる仕組みがPostgreSQLにも用意されたと言ったほうがイメージしやすいかもしれません。

詳しくは[ PostgreSQL Wiki ](https://wiki.postgresql.org/wiki/Future_of_storage)を御覧ください。
例としてMySQL/MariaDBが提供しているものだと、どれがPostgreSQLに必要かというのが書かれていたりするので参考になるかもしれません。

（余談ですが）PostgreSQL12から、psqlのメタコマンドでそのテーブルがどのIFを利用しているか確認することができます。デフォルトはもちろん「heap」となります。

```
postgres=# CREATE TABLE foo (id int,v text);
CREATE TABLE
postgres-# \d+ foo
                                    Table "public.foo"
 Column |  Type   | Collation | Nullable | Default | Storage  | Stats target | Description 
--------+---------+-----------+----------+---------+----------+--------------+-------------
 id     | integer |           |          |         | plain    |              | 
 v      | text    |           |          |         | extended |              | 
Access method: heap
```

# 1.2. zheapってなんだ？

いきなりzheapって書きましたが、今回は新たなストレージエンジンとして期待されているこの製品を試してみようと思います。PostgreSQL12では、コア機能として選択できるストレージエンジンは既存の1種類しかないですが、新たなストレージエンジンとしてEnterpriseDB社が絶賛開発しているものになります。


zheapについて詳しく知りたい方は、Amit Kapilaさんの発表資料があるので[コチラ](https://www.postgresql.eu/events/pgconfeu2018/sessions/session/2104/slides/93/zheap-a-new-storage-format-postgresql.pdf)をご確認ください。

zheapとは、PostgreSQLでUNDOログを利用したストレージフォーマットを実現するものです。EDB社が主導で開発を進めており、従来のheapと比べて、テーブルサイズの肥大化を抑えることができ、領域の即時再利用できるようにすることでVACUUM処理が不要になるというものらしい。

ちなみにソースはGithubで[公開](https://github.com/EnterpriseDB/zheap)されているので、合わせてご参照いただくのが良いかと思います。


# 2. zheapの動作確認

## 2.1. インストール～PG起動

現在、zheapはPostgreSQLから切り出された形にはなっていないので、zheap込みのPostgreSQLを新しくにインストールする必要があります。（現時点で12beta1がベースっぽいです）

```shell

cd ~
git clone https://github.com/EnterpriseDB/zheap
cd ~/zheap
PGHOME=~/pg_zheap/pgsql
./configure --prefix=${PGHOME} \
            --bindir=${PGHOME}/bin \
            --libdir=${PGHOME}/lib \
            --sysconfdir=${PGHOME}/etc \
            --includedir=${PGHOME}/include \
            --datadir=${PGHOME}/share \
            --with-pgport=5432 \
            --with-python \
            --with-openssl \
            --with-pam \
            --with-ldap \
            --with-libxml \
            --with-libxslt
cd src
make
make install
PGDATA=~/pg_zheap/data
~/pg_zheap/pgsql/bin/initdb -D ${PGDATA} --no-locale --encoding=utf8
~/pg_zheap/pgsql/bin/pg_ctl -D ${PGDATA} start
```

起動すると見たことないプロセスが動いていることに気づきます。

```console
$ ps -ef | grep postgres
ikki    8823     1  0 21:00 ?        00:00:00 /home/ikki/pg_zheap/pgsql/bin/postgres -D /home/ikki/pg_zheap/data
ikki    8825  8823  0 21:00 ?        00:00:00 postgres: checkpointer   
ikki    8826  8823  0 21:00 ?        00:00:00 postgres: background writer   
ikki    8827  8823  0 21:00 ?        00:00:00 postgres: walwriter   
ikki    8828  8823  0 21:00 ?        00:00:00 postgres: autovacuum launcher   
ikki    8829  8823  0 21:00 ?        00:00:00 postgres: stats collector   
ikki    8830  8823  0 21:00 ?        00:00:00 postgres: discard worker★
ikki    8831  8823  0 21:00 ?        00:00:00 postgres: undo worker launcher★
ikki    8832  8823  0 21:00 ?        00:00:00 postgres: logical replication launcher
```

追加されたプロセスの概要：

- discard worker
    - UNDOを非同期に管理として不要になったUNDOログの破棄を行うためのプロセス
- undo worker launcher
    - 必要に応じてUNDOアクションを実行するための undo worker を起動するランチャー


## 2.2. zheapを使用したテーブルの作成

GithubのREADME.mdのとおりに実行してみる。

```shell
postgres=# CREATE TABLE foo (id int, v text);
CREATE TABLE
postgres=# CREATE TABLE bar (id int, v text) USING zheap;
CREATE TABLE
postgres=# \d+ foo
                                    Table "public.foo"
 Column |  Type   | Collation | Nullable | Default | Storage  | Stats target | Description 
--------+---------+-----------+----------+---------+----------+--------------+-------------
 id     | integer |           |          |         | plain    |              | 
 v      | text    |           |          |         | extended |              | 
Access method: heap

postgres=# \d+ bar
                                    Table "public.bar"
 Column |  Type   | Collation | Nullable | Default | Storage  | Stats target | Description 
--------+---------+-----------+----------+---------+----------+--------------+-------------
 id     | integer |           |          |         | plain    |              | 
 v      | text    |           |          |         | extended |              | 
Access method: zheap
```

GUCパラメータの`default_table_access_method`を変更することでデフォルトを変更することもできます。あとはSET文でも指定可能なので、都合の良いやり方を選んべばOKです。（デフォルト値は'heap'になっています。）

```console
postgres=# show default_table_access_method;
 default_table_access_method 
-----------------------------
 heap
(1 row)
```

ちなみにUNDOログはこんな感じで出力されています。

```shell
$ ll -h pg_zheap/data/base/undo/
total 2.0M
-rw------- 1 ikki ikki 1.0M Jul  7 08:54 000000.0000300000
-rw------- 1 ikki ikki 1.0M Jul  7 08:52 000000.0000400000
```

作成されているときはログに出力されておりこういう出力内容でした。

```console
2019-07-07 08:53:32.542 UTC [11946] LOG:  created undo segment "base/undo/000000.0000100000"
2019-07-07 08:53:41.280 UTC [11946] LOG:  created undo segment "base/undo/000000.0000200000"
2019-07-07 08:53:48.824 UTC [11946] LOG:  created undo segment "base/undo/000000.0000300000"
2019-07-07 08:53:56.952 UTC [11896] LOG:  recycled undo segment "base/undo/000000.0000000000" -> "base/undo/000000.0000400000"
2019-07-07 08:53:56.952 UTC [11896] LOG:  unlinked undo segment "base/undo/000000.0000100000"
2019-07-07 08:53:56.952 UTC [11896] LOG:  unlinked undo segment "base/undo/000000.0000200000"
```



# 3. 性能比較

本当は有効に効くケース（テーブルが肥大化するようなケース？）とかを考えた上でやるべきですが、一旦は何も考えずにやってみる。

## 3.1. pgbench

heapで作ったテーブルとzheapで作ったテーブルで試してみる。
`default_table_access_method`はセッション単位であればSET文で変更してもよいし、`ALTER SYSTEM`でやってもOKです。

```console
postgres=# show default_table_access_method ;
 default_table_access_method 
-----------------------------
 zheap
(1 row)
```

```shell
# 初期テーブル作成
~/pg_zheap/pgsql/bin/pgbench -i -s 50 postgres

# 負荷掛け
~/pg_zheap/pgsql/bin/pgbench -c 10 -T 180 postgres
```

## 3.2. 結果

### TPS（大きな差分はない）

結果は以下のとおり。デフォルトのpgbenchの負荷では、ストレージエンジンの変更による性能差は見られなかったです。（3回実施した平均値として取得）

|method|latency avg|tps|
|:---|:---|:---|
|heap|6.912 ms|1446.818450|
|zheap|6.723 ms|1487.459050|


### テーブルサイズの変化（若干の差分あり）

上記のベンチマークを3回実行したあとのテーブルサイズを確認した結果は以下のとおりでした。
なぜか初期値が違うし、ばらつきもありますが増加量でいうと、heapだと10MB増加していたものが、zheapだと数KBに収まっており、たしかにテーブルの肥大化に対して効果がありそうだなという感じですね。

#### heapで作成したテーブルの場合

|table|before|after|
|:---|:---|:---|
|pgbench_accounts|641 MB|651 MB |
|pgbench_branches |40 kB|296 kB|
|pgbench_history|0 bytes|13 MB|
|pgbench_tellers|56 kB|368 kB|

#### zheapで作成したテーブルの場合

|table|before|after|
|:---|:---|:---|
|pgbench_accounts|514 MB|514 MB|
|pgbench_branches |48 kB|656 kB|
|pgbench_history|8192 bytes|9152 kB|
|pgbench_tellers|56 kB|784 kB|


# 4. まとめ
今回導入された `table_access_method` を変更することで、PostgreSQLユーザはそのワークロードに合わせたストレージエンジンのテーブルを作成できるようになりました。
この機能によって、将来的には、ワークロードの特性に合わせたストレージエンジンを選択することができ、ますますPostgreSQLの活躍の場を増やせそうで楽しみです。

今後ですが、これでチュートリアルは終わった認識なので、もう少しzheapの理解を深め、より顕著な差が出るケースを検討して、測定をしてみようかと思います。