pg_shardによるPostgreSQLのシャーディング

MySQLにはシャーディング機能があるらしいですが、PostgreSQLには実装されていません。
しかし、今回紹介する追加モジュール「pg_shard」によってシャーディング機能を使用することができます。

pg_shard ver.1.2.3　[https://github.com/citusdata/pg_shard](https://github.com/citusdata/pg_shard)

ただし、**pg_shardは現在推奨されておらず**、PostgreSQLをベースとした[citus DB](https://github.com/citusdata/citus)に開発を移したみたいです。

# シャーディングとは
データを複数のノードに分散させることで、マシンリソース（CPU、I/O）を効率よく使うことができる仕組みです。
大規模なデータを取り扱う場合には重要になる機能です。

図：データが分散されるイメージ
<img width="247" alt="pg_shard_分散のイメージ.PNG" src="https://qiita-image-store.s3.amazonaws.com/0/125929/74d0a349-e0b3-cf55-9e0d-e56ef3a7bd4a.png">


pg_shardの場合、Masterノードと呼ばれるサーバが受け取ったクエリを良しなに振り分けを行い、振り分けられた処理をWorkerノードが処理する仕組みになっています。

# 前提条件

以下、構築した内容です。

* CentOS 7.2
* PostgreSQL 9.4.7
* pg_shard 1.2.3

構成として、VMを3台使用しています。
pg_shardを使用したシャーディングを行う場合、最低でもDBは3台以上が必要になります。
<img width="256" alt="pg_shard_構成図.PNG" src="https://qiita-image-store.s3.amazonaws.com/0/125929/2a612697-13cd-3350-3c4a-e3d20e47a1b0.png">


# 構築手順

## 1. Workerノード作成
以下のWorkerノード構築の手順となります。
WorkerノードとなるPostgreSQLを2台を構築するため、最初にPostgreSQLをインストールします。方法は何でもOKで、yum使っても、直接RPMを使ってもソースからコンパイル等、好きなものを選んでください。

今回はyumを使ってPostgreSQLを構築します。

### 1-1. PostgreSQLのyumリポジトリ登録
以下の手順ではじめにPostgreSQLのyumリポジトリを登録します。

```
# wget https://download.postgresql.org/pub/repos/yum/9.4/redhat/rhel-7-x86_64/pgdg-centos94-9.4-2.noarch.rpm
# rpm -ivh pgdg-centos94-9.4-2.noarch.rpm
```

必要なときにだけ、リポジトリを有効にするため、以下のファイルの修正を行ってください。

```/etc/yum.repos.d/pgdg-94-centos.repo
[pgdg94]
・・・
#enabled=1
enabled=0
```

### 1-2. PostgreSQLのインストール
pg_shardはmakeする必要があるため、postgresql94-serverに加えて、postgresql94-devel、postgresql94-contribが必要になります。

```
# yum --enablerepo=pgdg94 install postgresql94-server postgresql94-devel postgresql94-contrib
```

### 1-3. 環境設定

初期設定として以下を実施する。

```
# su - postgres
$ vi ~/.bashrc

--以下の内容を追記---
export PATH=/usr/pgsql-9.4/bin:$PATH
--------------------

$ vi ~/.bash_profile

--以下の内容を追記---
if [ -f ~/.bashrc ]; then
    . ~/.bashrc
fi
--------------------
```

### 1-4. DBの作成
初期設定が終わったら、以下のコマンドを実行し、PostgreSQLを構築します。

```
$ source ~/.bashrc
$ initdb --no-locale --encoding=utf-8
$ vi $PGDATA/postgresql.conf
$ vi $PGDATA/pg_hba.conf
```

```$PGDATA/postgresql.conf
listen_addresses = '*'
shared_preload_libraries = 'pg_shard'
```

```$PGDATA/pg_hba.conf
host    all       all      192.168.1.0/24     trust
```

### 1-5. pg_shardのインストール
PostgreSQLのリポジトリには7/31現在では最新版のRPMが提供されていなかったので、ソースコンパイルを実施します。

```
$ wget https://github.com/citusdata/pg_shard/archive/v1.2.3.tar.gz
$ tar -xvf v1.2.3.tar.gz
$ cd pg_shard-1.2.3
$ make
$ sudo -H PATH=/usr/pgsql-9.4/bin:$PATH make install
```

### 1-6. Workerノード構築完了
構築が完了したら、起動しておきます。

```
$ pg_ctl start
$ createdb testdb
```

※あとで動作確認するためにtestdbという名前でDBを作っています。

## 2. Masterノード作成
手順は｢pg_shardのインストール｣まではWorkerノードと同じです。
異なるのは、**手順2-6以降のpg_shardの設定を行う箇所のみ**です。

### 2-1. PostgreSQLのyumリポジトリ登録

```
# wget https://download.postgresql.org/pub/repos/yum/9.4/redhat/rhel-7-x86_64/pgdg-centos94-9.4-2.noarch.rpm
# rpm -ivh pgdg-centos94-9.4-2.noarch.rpm
```

```/etc/yum.repos.d/pgdg-94-centos.repo
[pgdg94]
・・・
#enabled=1
enabled=0
```

### 2-2. PostgreSQLのインストール

```
# yum --enablerepo=pgdg94 install postgresql94-server postgresql94-devel postgresql94-contrib
```

### 2-3. 環境設定

```
# su - postgres
$ vi ~/.bashrc

--以下の内容を追記---
export PATH=/usr/pgsql-9.4/bin:$PATH
--------------------

$ vi ~/.bash_profile

--以下の内容を追記---
if [ -f ~/.bashrc ]; then
    . ~/.bashrc
fi
--------------------
```

### 2-4. DBの作成

```
$ source ~/.bashrc
$ initdb --no-locale --encoding=utf-8
$ vi $PGDATA/postgresql.conf
$ vi $PGDATA/pg_hba.conf
```

```$PGDATA/postgresql.conf
listen_addresses = '*'
shared_preload_libraries = 'pg_shard'
```

```$PGDATA/pg_hba.conf
host    all       all      192.168.1.0/24     trust
```

### 2-5. pg_shardのインストール

```
$ wget https://github.com/citusdata/pg_shard/archive/v1.2.3.tar.gz
$ tar -xvf v1.2.3.tar.gz
$ cd pg_shard-1.2.3
$ make
$ sudo -H PATH=/usr/pgsql-9.4/bin:$PATH make install
```

### 2-6. pg_shardの設定ファイルを作成

Workerノードで起動しているPostgreSQLへの接続情報（接続先ホスト、ポート番号）を書きます。

```$PGDATA/pg_worker_list.conf:
192.168.1.2 5432
192.168.1.3 5432
```

### 2-7. Masterノード起動

Masterノードを作成したら、シャーディングを行うDBを作成します。

```
$ pg_ctl start
$ createdb testdb
```

### 2-8. pg_shardモジュールのDBへの読み込み
作成したtestdbにpg_shardを読み込みませます。

```
$psql testdb -U postgres -c 'CREATE EXTENSION pg_shard'
```

これで、pg_shardの独自関数等がtestdb内で使用することができます。

### 2-9. シャーディングの設定を行う

#### 2-9-1. シャーディングの対象となるテーブルを作成する

```
$ psql testdb -U postgres -c 'CREATE TABLE shard_tbl (id int PRIMARY KEY,var varchar(30))'
```

#### 2-9-2. シャーディングを行う対象のテーブルとキーをpg_shardの独自関数で指定する。

```
$ psql testdb -U postgres -c "SELECT master_create_distributed_table('shard_tbl', 'id')"
```

#### 2-9-3. シャーディングの詳細（シャード数、レプリケーション数）を設定する。

```
$ psql testdb -U postgres -c "SELECT master_create_worker_shards('shard_tbl',2,1)"
```

|項目名|概要|
|:---|:---|
|シャード数|データを分散する数|
|レプリケーション数|データを複製する数|

今回はWorkerノードが2台あるので、2箇所に分散されテイク様子を確認したいのでシャード数に｢2｣、レプリケーション数に｢1｣を設定します。

### 2-10. Masterノード設定完了

これで準備は整いました。
あとはデータをINSERTして、挙動を確かめるだけの簡単なお仕事です。

# 3. 動作確認

## 3-1. 10件のデータをINSERTする

```
$ for i in `seq 0 9` ;do psql testdb -U postgres -c "INSERT INTO shard_tbl VALUES ($i,'XXXXXXXXX')"; done
```

色々試した結果、1行スクリプトでやることにしました。

## 3-2. 各ノードのデータをSELECTしてみる

* Masterノード

ちゃんと入力したデータは書き込まれていますね。

```
testdb=# select * from shard_tbl;                                                                
 id |    var    
----+-----------
  0 | XXXXXXXXX
  1 | XXXXXXXXX
  3 | XXXXXXXXX
  4 | XXXXXXXXX
  5 | XXXXXXXXX
  7 | XXXXXXXXX
  8 | XXXXXXXXX
  2 | XXXXXXXXX
  6 | XXXXXXXXX
  9 | XXXXXXXXX
(10 rows)
```

* Workerノード1

3件だけ書き込まれているようです。

```
testdb=# select * from shard_tbl_10006 ;
 id |    var    
----+-----------
  2 | XXXXXXXXX
  6 | XXXXXXXXX
  9 | XXXXXXXXX
(3 rows)
```

* Workerノード2

こちらは残りの7件が書き込まれていますね。

```
testdb=# select * from shard_tbl_10005 ;
 id |    var    
----+-----------
  0 | XXXXXXXXX
  1 | XXXXXXXXX
  3 | XXXXXXXXX
  4 | XXXXXXXXX
  5 | XXXXXXXXX
  7 | XXXXXXXXX
  8 | XXXXXXXXX
(7 rows)
```

なるほど、ちゃんとデータは分散されているようですね。

# 使ってみて気づいた点

結構実行できるクエリに制限がありそうです。
テストデータのINSERTを試しただけでも以下の制限に引っかかりました。

* サブクエリの制限

```
$ psql testdb -U postgres -c "INSERT INTO shard_tbl SELECT generate_Series(1,10),(random()*10000)::int%10000"
ERROR:  cannot perform distributed planning for the given query
DETAIL:  Subqueries are not supported in distributed queries.
```

* SQL関数による制限

```
$ for i in `seq 0 9` ;do psql testdb -U postgres -c "INSERT INTO shard_tbl VALUES ($i,random())"; done
ERROR:  cannot plan sharded modification containing values which are not constants or constant expressions
```

これはFDWとか等による制約でしょうか。
他にもいくつか制約があるようで公式の[マニュアル](https://github.com/citusdata/pg_shard)をご確認ください。