Db2:リストアを速くしたい

# 背景
毎日、Db2のバックアップから別サーバーにリストアしています。データの増加に伴い、リストアにかかる時間が長くなり、メンテナンスウィンドウに収まらなくなってきました。

# やりたいこと
リストアのパフォーマンスを上げたい。具体的には以下になります。
1. ボトルネックがどこかを知りたい
1. ボトルネックに対しリソース追加して処理時間を短縮したい

# 環境
1. バックアップ用ディスクからデータベース用ディスクにリストアします。
1. データベース用ディスクは性能が違う2種類(Normal, Performance Plus)で比較します。
1. [Performance Plus](https://learn.microsoft.com/ja-jp/azure/virtual-machines/disks-enable-performance?tabs=azure-cli "Azureウェブサイト")(以降、PP) はAzureマネージドディスクのオプションです。2024年1月29日時点でプレビューですが、同じサイズのディスクを追加料金なしでIOPSとスループットを上げることができます。余談ですが、作成直後はディスクサイズの半分以上を使用しないとIOPSとスループットが向上しないケースがありました。

```mermaid
flowchart LR
  disk1[(Backup)]-->dbms[Db2]
  dbms--> disk2[(Normal)] & disk3[(PP)]  
```

| 区分 | 製品サービス | サイズ |
|:-|:-|:-|
| クラウド     | Microsoft Azure  |  |
| 仮想マシン   | Standard E4ads v5 | 4vcpu, 32GiB memory |
| ディスク：データベース用 | Standard SSD Normal | 2TB,  500IOPS,  60MBps |
| ディスク：データベース用 | Standard SSD PP     | 2TB, 3000IOPS, 300MBps |
| ディスク：バックアップ用 | Standard SSD Normal | 1TB,  500IOPS,  60MBps |
| OS          | Ubuntu 20.04 LTS  |   |
| データベース | IBM Db2 V11.5  | 715GB |
|テーブルスペース数|Db2テーブルスペース|1179|
| バックアップ媒体 | ファイル  | 200GB: Db2でcompress  |

# 統計情報の用語
リストアが完了するとDB2DIAGに統計情報が出力されます。次節で統計情報を解説するので先に用語を説明します。

- Parallelism：処理の並列度
- Number of buffers：バッファー数
- Buffer size：バッファーサイズ
- BM#：BMのEDU ID、**BMとはバッファー・マニピュレーターでテーブルスペースの読み書きを担当**
- Total：合計処理時間（秒）      
- I/O：入出力にかかった時間（秒）     
- Decompr：圧縮されたバックアップの解凍にかかった時間（秒）    
- MsgQ：入出力バッファーを待機するために費やした時間（秒）
- WaitQ：状態マシン制御メッセージを待機するために費やした時間（秒）
- Buffers：処理した入出力バッファー数   
- MBytes：処理したデータ量
- MC#：MCのEDU ID、**MCとはメディア・コントローラーでバックアップ媒体の読み書きを担当**

詳細はIBMのマニュアルをご覧ください。
[バックアップおよびリストア統計](https://www.ibm.com/docs/ja/db2/11.5?topic=backup-restore-statistics "IBMウェブサイト")

# 解説

715GBのデータベースをリストアするのに7時間38分かかっています。リストア・コマンドは以下です。
```
db2 restore db $DB_NAME from $BASE_DIR taken at $NEW_DATE on /db2-1/db2 dbpath on /db2-1/db2 logtarget $LOG_DIR encrypt without prompting
```
パフォーマンスに関するパラメーターは指定していません。

```
2023-12-18-08.41.20.698539+540 E1803297E1468         LEVEL: Info
PID     : 3237                 TID : 139853517809408 PROC : db2sysc 0
INSTANCE: xxxxxxxx             NODE : 000            DB   : xxxxxxxx
APPHDL  : 0-38563              APPID: *LOCAL.xxxxxxxx.231217160341
AUTHID  : xxxxxxxx             HOSTNAME: xxxxxxxx
EDUID   : 25865                EDUNAME: db2agent (xxxxxxxx) 0
FUNCTION: DB2 UDB, database utilities, sqluxLogDataStats, probe:2051
MESSAGE : Performance statistics
DATA #1 : String, 944 bytes

Parallelism       = 2
Number of buffers = 4
Buffer size       = 16781312 (4097 4kB pages)
                                                                 
BM#    Total      I/O     Decompr    MsgQ      WaitQ      Buffers   MBytes 
---  --------  --------  --------  --------  --------    --------  --------
000  27457.85  25869.13   1478.84      0.57      6.01        5919     94711
001  27445.06  25877.28   1475.86      0.02     91.54        5940     95063
---  --------  --------  --------  --------  --------    --------  --------
TOT         -         -         -         -         -       11859    189774

MC#    Total      I/O      MsgQ      WaitQ      Buffers   MBytes 
---  --------  --------  --------  --------    --------  --------
000  27438.40     97.03  27341.21      0.03       11860    189774
---  --------  --------  --------  --------    --------  --------
TOT         -         -         -         -       11860    189774

```


統計情報から以下のことがわかります。
1. Parallelism=2, Number of buffers=4, Buffer size=4097*4kB をDb2が自動的に設定
1. BMが2行なので**並列度2**でテーブルスペースを書き込み
1. 各BMの処理時間はほぼ同じ、**1つのテーブルスペースは1つのスレッドでしか処理されないので**、巨大なテーブルスペースにデータが偏っているとBMの処理時間にばらつきが生じ、並行性が低下
1. BMの処理時間のほとんどはI/O、25,869秒は7.2時間
1. MCが1行なので並列度1でバックアップファイルを読み取り
1. MCの処理時間のほとんどはMsgQ、27,341秒は7.6時間

リストアはBMとMCの観点から説明すると以下になります。
1. MCがバックアップ媒体を読んで
1. MCがBMにバッファーを渡して
1. BMがテーブルスペースに書く

```mermaid
flowchart LR
  disk1[(Backup)]-->mc[mc]--> buffer(buffer)--> bm1[bm] & bm2[bm]
  --> disk2[(Tablespace)]
```

**MsgQの時間が長いということは相手を待っている状態です。** このケースですと、MCのMsgQの時間が長いのでMCは待ち状態になっています。つまり、**BMのI/Oの時間が長い＝テーブルスペースへの書き込みが遅い**ということになります。

# その他の情報

### リストア中のサーバーリソース
| 指標 | 値 |
|:-:|:-:|
|平均CPU使用率   | 6%  |
|平均書き込みスループット   | 13.4MiB/秒  |
|空きメモリー   |10Gib   |

### Db2が自動計算する並列度
いつもお世話になっているエンバー・クルックスさんのブログには、バックアップの並列度はDb2が5か10に自動計算されることが多いとあります。
[Speeding up DB2 Backups](https://datageek.blog/2015/09/22/speeding-up-db2-backups/ "DataGeek.blog")

### Azure Managed Diskの特性
シングルスレッド書き込みが弱いと以前に記事を書きました。記事はPremium SSDについてですがStandard SSDも変わらないのではないかと想像しています。マルチスレッドで書き込むとパフォーマンスが上がります。
[Azure Storage:Premium SSD はシングルスレッド書き込みが苦手？](https://qiita.com/tsunenorikan/items/87088a767af69faecd0d "Qiita")

# 対策
1. リストアの並列度を明示的に指定：parallelism xx
1. ディスクのIOPSとスループットを上げる：Performance Plusを有効化

```
db2 restore db $DB_NAME from $BASE_DIR taken at $NEW_DATE on /db2-1/db2 dbpath on /db2-1/db2 logtarget $LOG_DIR parallelism xx encrypt without prompting
```

# 結果
データベースディスクが通常のStandard SSD
| 並列度 | 処理時間 | CPU% | スループット |
|:-:|:-:|:-:|:-:|
| 2	| 7:38:24| 6%| 13MiB/秒 | 
| 5	| 5:48:45| 8%| 12MiB/秒 |
| 10| 5:28:14| 9%| 13MiB/秒 |
| 20| 5:08:00| 9%| 13MiB/秒 |
| 40| 4:53:02| 10%|13MiB/秒 |
| 60| 4:46:08| 10%|13MiB/秒 |
| 80| 4:42:34| 10%|14MiB/秒 |
| 100|4:39:56| 10%|13MiB/秒 |
| 120|4:36:58| 10%|14MiB/秒 |

データベースディスクがPerformance Plusを有効にしたStandard SSD
| 並列度 | 処理時間 | CPU% | スループット |
|:-:|:-:|:-:|:-:|
| 2	| 3:12:12| 13%| 22MiB/秒| 
| 5	| 1:51:52| 25%| 38MiB/秒| 
| 10| 1:52:19| 25%| 38MiB/秒| 
| 20| 1:31:19| 30%| 45MiB/秒| 
| 40| 1:33:55| 30%| 43MiB/秒| 
| 60| 1:20:14| 30%| 50MiB/秒| 
| 80| 1:17:16| 30%| 48MiB/秒| 
| 100|1:22:49| 29%| 42MiB/秒| 
| 120|1:17:54| 30%| 48MiB/秒| 

並列度10の統計情報
```
2023-12-20-02.59.24.858636+540 E23099E2079           LEVEL: Info
PID     : 3237                 TID : 139853522003712 PROC : db2sysc 0
INSTANCE: xxxxxxxx             NODE : 000            DB   : xxxxxxxx
APPHDL  : 0-5065               APPID: *LOCAL.xxxxxxxx.231219160729
AUTHID  : xxxxxxxx             HOSTNAME: xxxxxxxx
EDUID   : 27141                EDUNAME: db2agent (xxxxxxxx) 0
FUNCTION: DB2 UDB, database utilities, sqluxLogDataStats, probe:2051
MESSAGE : Performance statistics
DATA #1 : String, 1554 bytes

Parallelism       = 10
Number of buffers = 18
Buffer size       = 16781312 (4097 4kB pages)
                                                                 
BM#    Total      I/O     Decompr    MsgQ      WaitQ      Buffers   MBytes 
---  --------  --------  --------  --------  --------    --------  --------
000   6714.75   6322.11    315.59      0.01      4.28        1136     18164
001   6706.98   6323.90    317.31      0.00     65.55        1137     18196
002   6706.75   6303.23    337.58      0.00     65.54        1222     19556
003   6705.58   6310.64    328.96      0.00     65.55        1171     18740
004   6704.87   6320.93    317.93      0.00     65.55        1169     18708
005   6706.73   6296.56    344.21      0.00     65.54        1249     19988
006   6705.44   6319.50    320.17      0.00     65.54        1134     18148
007   6704.52   6306.13    332.53      0.00     65.54        1202     19236
008   6703.36   6307.10    330.39      0.00     65.54        1210     19364
009   6703.48   6292.40    345.20      0.00     65.54        1254     20068
---  --------  --------  --------  --------  --------    --------  --------
TOT         -         -         -         -         -       11884    190174

MC#    Total      I/O      MsgQ      WaitQ      Buffers   MBytes 
---  --------  --------  --------  --------    --------  --------
000   6695.42    100.80   6594.37      0.10       11885    190174
---  --------  --------  --------  --------    --------  --------
TOT         -         -         -         -       11885    190174
```

# 評価
1. 並列度を上げるとBMが増えました。結果、並列でテーブルスペースに書き込みができ、リストア時間が短縮されました。
1. リストア時の並列度をDb2に自動計算させると2になり、Azureの環境では最適化されませんでした。
1. データベースディスクのIOPSとスループットを上げると書き込み能力が上がって、リストア時間が短縮されました。並列度を上げると向上したIOPSとスループットをさらに活かすことができます。