本当にいまさらだけど、syncdbの性能を測ってみる。

# 1. はじめに
NTTテクノクロスの上原です。[弊社アドベントカレンダー](https://qiita.com/advent-calendar/2019/ntt-tx)の8日目の記事を書きます。

簡単に自己紹介ですが、自分は現在PostgreSQL 5年生で、弊社では[PostgreSQL関連の業務](https://www.ntt-tx.co.jp/products/osscloud/service/ticket/?utm_source=qiita&utm_medium=ac2019&utm_campaign=08)（設計コンサル、トラブルシュート等のサポートやDBの移行支援等）を担当しております。
最近では、オンプレ環境だけでなく、AWS上の移行案件についての対応も増えてきております。

今回は今更ではありますが、[syncdb](http://ossc-db.github.io/syncdb/index.html)というツールをDB移行に利用するケースを想定した際の性能についてお話させていただきます。

## What is syncdb?

syncdbとは、PostgreSQL、Oracleを対象に任意のテーブルを同期する機能を提供するOSS製品です。

同じDBはもちろん、異種DB間であっても任意のテーブルを同期することができるという画期的な機能を提供します。
同期機能は「全体リフレッシュ」、「差分リフレッシュ」の2つが提供されています。

- 全体リフレッシュ
  - 初回同期時や更新量が多い場合に選択される同期方式で同期対象のテーブルをTRUNCATEしテーブルを全体のINSERTを行う。
- 差分リフレッシュ
  - 更新量がすくない場合に選択される同期方式でsyncdbのメイン機能。
  - 記録された差分ログ情報に基づき、同期先のテーブルを操作する。

syncdbの用途としては、テーブルの冗長化、異種DB間の連携やDB移行に利用することができます。

大雑把にですが、こんなイメージです。
![syncdb_image.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/125929/b0611590-e5fe-1025-7bcf-2918992c6484.png)

※処理の詳細はマニュアルに記載されているので、詳しくは[コチラ](http://ossc-db.github.io/syncdb/index.html#syncdb-img)を御覧ください。

## なぜ、syncdbの性能を確認したいの？
ツールをご存知の方は何を今更と思うでしょう。
実はこのツールは約10年前に公開されており、PostgreSQLでいうと「8.4」あたりの時代作られたものです。（ちなみに自分は今回初めてsyncdbを触りました汗）

最初に紹介させていただいたとおり、自分はDBの移行支援の業務を行っているのですが、移行の方式検討に対して「syncdbって検討してみた？」というコメントを内部で指摘され、もしかしたらハマるパターンがあるような気がして、モヤモヤを抱えておりました。

そんな中、アドベントカレンダーの時期がやってきたので、良い機会なのでこのモヤモヤを晴らそうと本テーマを選びました！

**前置きが長くなってしまいましたが、ここから本題です！**

# 2. 実施概要
今回は、比較的規模の大きいデータベース移行でsyncdbを利用できるかどうか指標となる値を取得する。という観点で測定を行っています。

そのため、今回の測定では <font color="Crimson">**「全体リフレッシュ」についてのみ**</font> 測定しています。

syncdbは本来の用途（差分リフレッシュができる）とは異なる使い方なので、結果はあくまで参考情報である点に注意してください。

## 環境
検証はAWS上で実施しています。

- PostgreSQLのバージョン

|sync元|sync先|
|:---:|:---:|
|PostgreSQL 8.4|Aurora PostgreSQL<br> (compatible with PostgreSQL 10.7)|

※移行を想定して、sync元は敢えて古いバージョンのものを利用しています。

- テーブルサイズ：800MB、4GB
  - ある程度傾向が掴めれば良いので、この2点で測定します。

- syncdbは移行元環境のEC2上で実行します。

## 測定パターン
以下の4つのパターンで測定を行いました。

|Case|概要|sync元（EC2）<br> インスタンスタイプ|sync先(Amazon Aurora)<br> インスタンスタイプ|テーブルサイズ|
|:--|:--|:--:|:--:|:--:|
|1|ベースライン取得|t2.micro|db.r5.large|800MB*1テーブル, <br> 4GB*1テーブル<br>（2パターン）|
|2|インスタンス強化|m5.large|db.r5.large|4GB*1テーブル<br>（1パターン）|
|3-1|インスタンス強化<br> syncdbの並列実行|c5.2xlarge|db.r5.large|800MB*5テーブル<br>（1パターン）|
|3-2|インスタンス強化<br> syncdbの並列実行<br> DBインスタンス強化|c5.2xlarge|db.r5.xlarge|800MB*5テーブル<br>（1パターン）|



# 3. 検証結果

## 3.1. テーブルサイズと処理時間の傾向

- 結果：**1GBあたり約3分**
- 処理時間とリフレッシュ対象のテーブルサイズはほぼ完全に比例することを確認した。

![case01_reflesh.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/125929/20c6ae8e-9a4a-4271-c647-df47db7a5d1a.png)


## 3.2. 条件による処理時間の変化

- 5並列環境において、**1GBあたり約1分**
- CPUリソースに余裕があれば、複数テーブルを同時に処理させることで処理時間の短縮は見込めるとわかった。
  - CPUリソースに余裕がない状態では並列化の効果は薄かった。（Case3-1）
  - リソースに合う並列化であれば、単位時間あたりの処理性能を上げることができた。（Case3-2）

![result.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/125929/7d38323d-72bd-8dd0-5ae0-f4f05ee86141.png)



---

# 4. 検証手順（以下、参考情報です。）
以降は、測定の手順や結果の詳細について記します。興味があれば、読んでみてください。
## 4.1. 事前準備
### 初期データ投入

```console
$ createdb sf60
$ createdb sf300
$ pgbench -i -s 60 sf60
```

### syncdb構築

syncdbのマニュアルを参考に以下のコマンドを実行していく。

```console
# syncdb用のモジュールをDBに登録する
$ createlang plpgsql sf60
$ createlang plpgsql sf300
$ psql sf60 -f ~/adventCalendar/syncdb-1.0.3/sql/mlog_postgresql.sql

# sync先のDBにもモジュールを登録する
$ psql -U postgres -h <Aurora Endpoint> -f ~/adventCalendar/syncdb-1.0.3/sql/observer_postgresql.sql rep_sf60
```

設定ファイルは以下のように定義した

```jdbcResource.xml 
<?xml version="1.0" encoding="UTF-8"?>
<SyncDatabase>
        <jdbcResource>
                <name>ec2_sf60</name>
                <className>org.postgresql.Driver</className>
                <url>jdbc:postgresql://localhost:5432/sf60</url>
                <username>uehara</username>
                <password></password>
        </jdbcResource>
        <jdbcResource>
                <name>aurora_sf60</name>
                <className>org.postgresql.Driver</className>
                <url>jdbc:postgresql://<Aurora Endpoint>:5432/rep_sf60</url>
                <username>postgres</username>
                <password>postgres</password>
        </jdbcResource>
</SyncDatabase>
```

### リフレッシュに向けた準備

syncされる側には予めsync元と同じテーブルスキーマが登録されている必要があるので、テーブルを作成しておきます。

```console
$ psql rep_sf60 -Upostgres -h <Aurora Endpoint> -c 'CREATE TABLE pgbench_accounts(aid int, bid int, abalance int, filler char(84), PRIMARY KEY(aid))'
```

```console
$ syncdb create --master ec2_sf60 --schema public --table pgbench_accounts
$ syncdb attach --master ec2_sf60 --server aurora_sf60 --schema public --table pgbench_accounts
input query : SELECT aid, bid, abalance, filler FROM "public"."pgbench_accounts"<改行>を入力
<ctrl+d>を入力
```

## 4.2. 測定結果
### Case1：処理時間のベースライン取得

Case1では、DBサイズを800MBと4GBの2パターンでの処理の時間を測定します。

```console
$ date; time syncdb refresh --server aurora_sf60 --schema public --table pgbench_accounts --mode auto; date
```

#### 検証条件

- EC2:t2.micro
- Amazon Aurora：db.r5.large
- テーブルサイズ
  - 800MB*1テーブル
  - 4GB*1テーブル

#### 結果
以下のとおりで、800MBで3分弱でした。

```console
Mon Dec  2 02:37:05 UTC 2019
INFO  - full refresh (insert:6,000,000)

real    2m46.556s
user    0m42.540s
sys     0m2.239s
Mon Dec  2 02:39:51 UTC 2019
```

4GBだと、およそ14分という結果でした。

```console
Mon Dec  2 02:47:48 UTC 2019
INFO  - full refresh (insert:30,000,000)

real    13m48.802s
user    3m22.874s
sys     0m9.948s
Mon Dec  2 03:01:37 UTC 2019
```

つまり、データ量を5倍にすると処理時間もきっかり5倍となり、**データサイズと処理時間が比例している**ことが確認できました。
もし、1TBのテーブルを移そうと思うと、、、60時間！？

![table-size.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/125929/ab0aa483-f679-79ba-0568-83dd7addf077.png)

あかんやつだ。。。

### Case2：インスタンスタイプ強化

さすがにCase1の結果だと、悲しい結果なので、なんとか早くできる方法がないか試してみようと思います。

AWSのパフォーマンスインサイトの情報を確認する限り、特にDB側がネックになっている様子は見えなかったので、EC2側のインスタンスを強化して再測定を行いました。

#### 検証条件
- EC2:m5.xlarge
- Amazon Aurora：db.r5.large
- テーブルサイズ：4GB*1テーブル

#### 結果

```
Mon Dec  2 09:48:10 UTC 2019
INFO  - full refresh (insert:30,000,000)

real    12m49.120s
user    2m48.374s
sys     0m16.408s
Mon Dec  2 10:00:59 UTC 2019
```

**ほぼ効果なし！**

<font color="Silver">Case2は、一応やってみたけど、実施前にEC2側のSar、Auraraのパフォーマンスインサイトを確認した限り特にボトルネックになっている部分は見られなかったので、これは妥当な結果といえる。（じゃあ、なんでやったんだよ！）</font>


### Case3-1：syncdbの並列実行

テーブル単位で並列実行することで処理効率を上げて、総処理時間の短縮を図ることができるか確認します。

ちなみに、並列実行機能はないので、以下のように単にsyncdbコマンドを同じタイミングで複数回実行する形で検証しました。

```shell
for i in 01 02 03 04 05
do
  syncdb refresh --server aurora_sf60 --schema public --table pgbench_accounts${i} --mode auto & > /dev/null 2>&1
done
```

#### 検証条件
- EC2:c5.2xlarge
- Amazon Aurora：db.r5.large
- テーブルサイズ：800MB*5テーブル

#### 結果

```
Mon Dec  2 10:40:43 UTC 2019
INFO  - full refresh (insert:6,000,000)

real    9m17.262s
user    0m30.870s
sys     0m1.954s
Mon Dec  2 10:50:01 UTC 2019
```

**思っていたほどの効果はない！**

あれ？9分って、これじゃあ、そんなに処理時間が変わらない。。。5並列なのに、25%程度の改善でしかない。

パフォーマンスインサイトを見ると、Aurora側のCPUがカツカツになっていることが見える。まぁ、syncdbを並列実行してAuroraに対する同時接続数が増えているので当然の結果ですね。

### Case3-2：syncdbの並列実行（DBインスタンス強化）

テーブル単位で並列実行することで処理効率を上げるため、Aurora側のCPU数を2倍にして、総処理時間の短縮を図ることができるか確認します。

#### 検証条件

- EC2:c5.2xlarge
- Amazon Aurora：db.r5.xlarge
- テーブルサイズ：800MB*5テーブル

#### 結果

```
Thu Dec  5 02:29:01 UTC 2019
INFO  - full refresh (insert:6,000,000)

real    5m6.538s
user    0m31.838s
sys     0m1.555s
Thu Dec  5 02:34:07 UTC 2019
```

**素晴らしい！！**

Case3-1では、syncdbの並列数に対してAurora側のCPU数が少ない状態でしたが、この検証でCPU数を倍にしたことでネックがある程度解消され、処理時間を短縮することができました！

<font color="DarkGray">とはいえ、syncdb5並列に対して、Auroraは4CPUという条件なので、パフォーマンスインサイトを見るとまだCPUがネックになっているようでした。インスタンスタイプを上げてCPU数を増やせば、改善はできそうですが、感触は掴めたので今回はここまで。</font>


## 4.3. 検証で実施できていないこと
- 差分リフレッシュの測定
  - syncdbの肝の機能なので、本当はこちらについても測定したほうがよいのですが本記事ではここまでとなります。

# 6. さいごに
モヤモヤはなくなり、すっきりしました。

もともとの検証の動機だった、「対応した案件にハマるかも」という疑問に対しては「条件が合えば可能」というのが結論です。（はっきりしない結論で恐縮です。。。）

- 採用の判断の際、以下の内容には注意する必要がある。
  - 例えば、1テーブルでTBオーダのものが存在するようなケースでは難しい。（検証結果からすると60時間コースになってしまう）
  - テーブル毎に差分ログテーブルを用意する必要があるので、数百テーブルあるシステムではセットアップのコストが高くなる。（部分的にsyncdbを使うというのは可能）
  - 同期対象テーブルの更新量が少ないか？多いと差分リフレッシュはできず全体リフレッシュになってしまう。

移行方式の検討段階で、「移行元のバージョンが古すぎてベンダの提供する移行サービス等を利用できない」といった問題があれば、 syncdb の利用を検討してみてはどうでしょうか？
全テーブルをsyncdbの対象とはせず、一部のテーブルだけはsyncdbを利用するというようなハイブリットな構成であれば、対応できるケースも増えると思います。

明日は[戸部さん](https://qiita.com/etctaro)によるJetpack Composeに関する記事が投稿される予定です。お楽しみに！