AWS の RDS（PostgreSQL）で Graviton2 インスタンスを試してみた

これは **[PostgreSQL Advent Calendar 2020](https://qiita.com/advent-calendar/2020/postgresql)** 25 日目のエントリです。

昨日は **[Yugo Nagata (yugo-n)](https://qiita.com/yugo-n)** さんでした。

 - **[PostgreSQL に Santa Clause（サンタ句）を実装する](https://qiita.com/yugo-n/items/38c6a268be3032a32930)**

AWS のサービスをあまり追いかけていない間（10 月頃？）に RDS の Graviton2 プロセッサ対応が GA になっていました。

**[MySQL Advent Calendar 2020](https://qiita.com/advent-calendar/2020/mysql)** のほうでは EC2 の Graviton2 インスタンスに MySQL を入れて試しましたが（**「[MySQL 8.0.22 で innodb_log_writer_threads の効果を見てみる](https://qiita.com/hmatsu47/items/06489ef05bfcaaf310f3)」**）、こちらでは RDS の PostgreSQL 12.4R1 で試してみます。

# テスト内容

**[第 19 回 PostgreSQL アンカンファレンス @ オンライン](https://pgunconf.connpass.com/event/194291/)**のネタで（db.m5.xlarge インスタンスに対して）試した内容を db.m6g.xlarge インスタンスを使って再現し、db.m5.xlarge インスタンスの結果と比較しました。

 - **[MySQL と PostgreSQL と主キー](https://speakerdeck.com/hmatsu47/mysqltopostgresqltozhu-ki)**
   - 中小規模の MySQL と PostgreSQL では、主キーにシーケンス値（`AUTO_INCREMENT` / `serial`）を使うべきか UUIDv4 を使うべきか（実験してみた）、という話。

先に記しておくと、中小規模・書き込みノード 1 つの PostgreSQL では、UUIDv4 よりもシーケンス値（`serial`）を使ったほうが高速で、かつ安定していました。
今回は db.m6g.xlarge（Graviton2）と db.m5.xlarge（Intel x86_64）の間で性能差と特性の違いの有無を見てみました。

## テスト環境

 - Amazon(AWS) RDS（PostgreSQL 12.4 R1）
 - db.m6g.xlarge / db.m5.xlarge インスタンス（いずれも 4vCPU / メモリ 16GiB）を比較
 - ストレージは SSD 700GiB
 - Single AZ
 - デフォルトパラメータグループ

## テスト内容詳細

中小規模のシステム想定でひたすら行挿入をしたときの、主キーの種類別パフォーマンスを比較するものです。
今回は主キーの種類別に、Graviton2 と Intel x86_64 の速度を比較しつつ、それぞれの特性に違いがないか確認しました（MySQL との比較は外しました）。

 - 120 万行のデータを 24 並列で挿入（合計 2,880 万行）
 - 主キーとして以下の 3 種類を使い、挿入 100 万行あたりの所要時間の推移を確認
   - (ⅰ) `serial`
   - (ⅱ) UUIDv4（文字列）
   - (ⅲ) UUIDv4（`uuid`型）
 - テストに使うテーブルとして、以下の 2 種類を用意
   - (1) 主キー列 + `integer`列 + `timestamp`列
   - (2) 主キー列 + `integer`列 + `varchar(100)`列（デフォルトで 100 文字入れる） + `timestamp`列

### テストに使用したテーブル

 - **(1) 主キー列 + `integer`列 + `timestamp`列**

```sql:テーブル定義(1)
-- (ⅰ) serial
CREATE TABLE btree_test2 (id serial PRIMARY KEY NOT NULL, v1 integer, t1 timestamp(6) NOT NULL DEFAULT current_timestamp);

-- (ⅱ) uuid_txt
CREATE TABLE btree_test3 (id char(36) PRIMARY KEY NOT NULL, v1 integer, t1 timestamp(6) NOT NULL DEFAULT current_timestamp);

-- (ⅲ) uuid
CREATE TABLE btree_test4 (id uuid PRIMARY KEY NOT NULL, v1 integer, t1 timestamp(6) NOT NULL DEFAULT current_timestamp);
```

 - **(2) 主キー列 + `integer`列 + `varchar(100)`列 + `timestamp`列**

```sql:テーブル定義(2)
-- (ⅰ) serial
CREATE TABLE btree_test2 (id serial PRIMARY KEY NOT NULL, v1 integer, t1 timestamp(6) NOT NULL DEFAULT current_timestamp, s1 varchar(100) DEFAULT '1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890');

-- (ⅱ) uuid_txt
CREATE TABLE btree_test3 (id char(36) PRIMARY KEY NOT NULL, v1 integer, t1 timestamp(6) NOT NULL DEFAULT current_timestamp, s1 varchar(100) DEFAULT '1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890');

-- (ⅲ) uuid
CREATE TABLE btree_test4 (id uuid PRIMARY KEY NOT NULL, v1 integer, t1 timestamp(6) NOT NULL DEFAULT current_timestamp, s1 varchar(100) DEFAULT '1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890');
```

### テストに使用した SQL（文）の一部

```sql:SQL（文）
-- (ⅰ) serial
INSERT INTO btree_test2 (v1) VALUES (10), (11), (12), (13), (14), (15), (16), (17), (18), (19);
-- これを 12 万行 1 ファイル × 24 種類用意

-- (ⅱ) uuid_txt
INSERT INTO btree_test3 (id, v1) VALUES ('23ce3e9f-16de-4e5c-a293-6104ca45b47a', 10), ('ccf75ce2-49e9-4585-8561-9c55acf9e954', 11), ('493ec339-a21c-4ae9-be52-8c313758dd77', 12), ('aee11198-7767-42a6-9363-a16556d936b7', 13), ('7133eb96-fe19-4d45-b2f7-62ddc719234d', 14), ('cc1ae2ee-d99e-4102-9288-877eaae3bddc', 15), ('67fc7a86-b3c8-46fa-a237-d6479ee693b5', 16), ('c2e663e1-0168-4084-97fb-97b7d4575cf6', 17), ('602f2007-76f4-45d7-8a0f-e51430943afe', 18), ('f2b1ce06-db4a-48c4-b089-94198d3ddbe9', 19);
-- これを（異なる UUID で）12 万行 1 ファイル × 24 種類用意

-- (ⅲ) uuid
INSERT INTO btree_test4 (id, v1) VALUES ('23ce3e9f-16de-4e5c-a293-6104ca45b47a'::uuid, 10), ('ccf75ce2-49e9-4585-8561-9c55acf9e954'::uuid, 11), ('493ec339-a21c-4ae9-be52-8c313758dd77'::uuid, 12), ('aee11198-7767-42a6-9363-a16556d936b7'::uuid, 13), ('7133eb96-fe19-4d45-b2f7-62ddc719234d'::uuid, 14), ('cc1ae2ee-d99e-4102-9288-877eaae3bddc'::uuid, 15), ('67fc7a86-b3c8-46fa-a237-d6479ee693b5'::uuid, 16), ('c2e663e1-0168-4084-97fb-97b7d4575cf6'::uuid, 17), ('602f2007-76f4-45d7-8a0f-e51430943afe'::uuid, 18), ('f2b1ce06-db4a-48c4-b089-94198d3ddbe9'::uuid, 19);
-- 同上
```

# テスト結果

## (1) 主キー列 + `integer`列 + `timestamp`列

まずは「サイズの小さい行をひたすら挿入」のパターンです。

![graph1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/123899/d4952b7b-1b01-9306-5530-9c285dd0b0db.png)

(I) が Intel x86_64、(G) が Graviton2 です。挿入 100 万行ごとに所要時間をプロットしています。縦軸は秒数です。
前述のとおり、主キーが`serial`のときに一番速く、かつ安定しています。

Intel x86_64 と Graviton2 の比較では、主キーが`serial`の場合は Intel x86_64 のほうが速くなりました。
逆に、主キーが`char(36)` の UUIDv4 では、わずかに Graviton2 のほうが速くなりました。

## (2) 主キー列 + `integer`列 + `varchar(100)`列 + `timestamp`列

(1) よりも 1 行のサイズが大きいパターンです。

![graph2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/123899/69ff1c13-f371-15d0-26fa-ac0ca0c90e8a.png)

(1) と同じで主キーが`serial`のときに一番速く、かつ安定しています。

なお、こちらは Intel x86_64・Graviton2 の間での差がほとんどなくなりました。
例外として、主キーが`char(36)` の UUIDv4 のみ Graviton2 のほうが速くなりました。

## 【参考】テスト実行時のパフォーマンスインサイト

こんな感じになりました。
![db_pi_g2.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/123899/2d60712b-ae7d-2852-98f6-c66e1b21cfc2.jpeg)

## 【おまけ 1 】(1) の (ⅲ) で、主キーの生成に`uuid-ossp`の`uuid_generate_v4()`を使用すると…

「Graviton2 など Arm プロセッサでは乱数のエントロピー不足による性能問題が発生しやすい」という話がありましたので、念のため`uuid-ossp`を使って UUIDv4 を PostgreSQL 側で生成するパターンも試してみました。

 - **【参考】[Amazon EC2 を Arm に切り替えたら幸せなことしかありませんでした](https://developers.cyberagent.co.jp/blog/archives/27782/)（CyberAgent Developers Blog）**

```sql:別DBを用意してテーブル作成
CREATE EXTENSION "uuid-ossp";
CREATE TABLE btree_test2 (id uuid PRIMARY KEY NOT NULL DEFAULT uuid_generate_v4(), v1 integer, t1 timestamp(6) NOT NULL DEFAULT current_timestamp);
```

※SQL（文）は (ⅰ) serial と同じものを使用。

![graph1b.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/123899/cf3b4c18-d94b-50ee-5595-58a86d33e9dd.png)

UUIDv4 の生成にわずかに時間が掛かるものの、明確な処理の滞留は見られませんでした。

※主キーが`uuid`型の場合、挿入行数が増えてくると`uuid-ossp`を使わなくても処理が遅くなり単位行あたりの所要時間のばらつきも大きくなりましたが、`uuid-ossp`を使ってもその傾向はほぼ変わりませんでした。

## 【おまけ 2 】RDS でのインスタンスタイプ（インスタンスクラス）の変更

EC2 の場合とは異なり、Intel x86_64 と Graviton2 の間でも変更可能です。

![db_chg_g2_intel.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/123899/73d063ea-9bfe-12c1-af1d-ef00d0b1fb54.jpeg)

# まとめ

EC2 インスタンスに MySQL 8.0.22 を入れて試したときは、

 -  2vCPU までなら Graviton2 が高速
 -  8vCPU 以上なら概ね Intel x86_64 が高速

でしたが、今回の RDS PostgreSQL の 4vCPU 環境では**「ほぼ同等の性能」**という結果が出ました。

今回試したワークロードはある意味特殊ですのでこれだけで全てを判断することはできませんが、~~概ね MySQL on EC2 の結果と整合していますので、なんとなく傾向は掴める…かもしれません~~2020/12/26 の追記にある`pgbench`の結果をあわせて考えると、MySQL on EC2 の結果とは少し傾向が異なる模様です。

また、今後 Graviton2 への最適化が進むとさらに結果は変わるかもしれません。

※実環境で使う場合は、使い方に合わせて性能調査を行いましょう。

---

**2020/12/26 追記：**
さすがにワークロードが`INSERT`に偏りすぎなので、本編とは別に、`pgbench`も掛けてみました。
ただし、あえてメモリに載る容量を意図してフィルファクタは`600`としています。

 - **テーブル初期化**

```sql:テーブル初期化
pgbench -i -s 600 -U postgres -h 【RDSエンドポイント】 -d pgbench
```

 - **ベンチマーク実行**

```sql:ベンチマーク実行
pgbench -N -r -c 【スレッド数】 -j 【スレッド数】 -T 300 -U 【RDSエンドポイント】 -d pgbench
```

 - **m6g.xlarge vs m5.xlarge**

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/123899/bf998f68-6ac7-63b2-a603-ff9f69538623.png)

縦軸は TPS、横軸はスレッド数です。4vCPU・メモリ 16GiB の m6g.xlarge と m5.xlarge では m6g.large（Graviton2）のほうが良い結果が出ました。

 - **m6g.4xlarge vs m5.4xlarge**

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/123899/5193df5b-f917-309b-135c-1f40af70c054.png)

16vCPU・メモリ 64GiB の m6g.4xlarge と m5.4xlarge でも m6g.4xlarge（Graviton2）のほうが良い結果が出ましたが、40 スレッド以上では SSD の IOPS 上限（書き込み約 14,000）に当たってしまいました（プロビジョンドの SSD を使ってベンチマークを取る必要がありますね）。

より vCPU の多いインスタンスでプロビジョンドの SSD を使って確認する必要がありますが、`pgbench`の結果は MySQL 8.0.22 on EC2 で`mysqlslap`を実行したときとは少し傾向が違うようです（`mysqlslap`の`mixed`では並列スレッド数を増やしても IOPS が上限に張り付く状況にはなりませんでした）。

---

**2020/12/26 追記その 2：**

RDS MySQL を使って「追試」してみました。

 - **[RDS Graviton2 インスタンスを試してみた（MySQL 編）](https://qiita.com/hmatsu47/items/bbe1034b078435c31d00)**