はじめに
私は現在 DBRE として働いています。
最近業務中に、素人質問ですが、という怖い前置きの後以下の質問をもらいました。(ちょっと短くまとめていますが大体の内容はこんな感じ)
「Aurora のクローンってコピーオンライトなんですね。クローンした DB の I/O ワークロードはクローン元の DB に Disk の負荷とかはかけないのですか?」
私はデータベース未経験の状態から今の部署に配属されており、私も素人なんだよな、と思いながら Aurora について色々読み漁って調べてみることにしました。
その内容が個人的に勉強になったので、この記事でまとめていこうと思います。
AWS 公式から出ている結論
まず、AWS の公式見解を確認しておきます。
Aurora クローンを使用すれば、本番環境に影響を与えずに迅速にテスト環境を構築でき、1TB 以上のデータを追加する作業もスムーズに行えます。クローンに対する変更は元のデータベースに影響を与えないため、安全に負荷テストを実施できます。
出典: Aurora クローンと DB スナップショットの違い - AWS re:Post
結論としては「影響なし」です。
でも、コピーオンライトということはストレージを共有しているのでは?それなのになぜ影響がないのか、という疑問が残ります。
以降では、公式ドキュメントと論文から分かった事実と、そこから考えられる仮説を整理していきます。
コピーオンライトとは
コピーオンライト(Copy-on-Write)は、データをコピーする際に実際のデータブロックを即座にコピーせず、元のデータへの参照を共有する技術です。書き込みが発生した時点で初めてその変更対象のブロックのみをコピーして新しい領域に書き込みます。
AWS ドキュメントより、Aurora クローンはこのコピーオンライトプロトコルを使用している子使用していることがわかります。
Aurora クローンの場合、クローン作成時は、元のストレージボリュームへのポインタを共有するだけであり、クローン側で書き込みが発生すると、その変更ブロックのみ新規にストレージを割り当てるようです。
クローン機能は Aurora ストレージレイヤーで実装されている copy-on-write プロトコルを使用します。
文献から分かったこと
書き込み後は別のストレージボリュームが作られる
AWS ドキュメントより、クローン作成後に変更が発生すると別のストレージボリュームが生成されることが分かります。
ソースとクローンのそれぞれに変更を加えると、新しいデータに対応する新しいストレージボリュームが作成されます。
つまり、アクセス先についてまとめると以下のような動作になります。
クローン作成直後
| DB | アクセス先 |
|---|---|
| 元 DB | 共有ストレージボリューム |
| クローン | 共有ストレージボリューム |
書き込み発生後
| DB | アクセス先 |
|---|---|
| 元 DB | 元 DB 用の変更分 + 共有ストレージボリューム |
| クローン | クローン用の変更分 + 共有ストレージボリューム |
DB インスタンスとストレージ層は分離している
Amazon Aurora: Design Considerations for High Throughput Cloud-Native Relational Databases (SIGMOD 2017) を読んでみました。
ここには Aurora の設計思想がまとめられています。
INTRODUCTION より、DB インスタンスとストレージ層は分離されていると明言されています
We use a novel service-oriented architecture (see Figure 1) with a multi-tenant scale-out storage service that abstracts a virtualized segmented redo log and is loosely coupled to a fleet of database instances
従来の RDS(EBS ベース)との違いを図にすると
Aurora のログベース設計
論文の「THE LOG IS THE DATABASE」の章で、Aurora のアーキテクチャが詳しく説明されています。
従来の MySQL の問題
従来の MySQL は 1 回のアプリケーション書き込みに対して、多くの I/O を発生させます:
- Redo Log(WAL)
- Binary Log
- Data Pages
- Double-Write Buffer(torn page 防止)
- FRM Files(メタデータ)
これらすべてをレプリケーションすると、I/O が膨大に増幅されます。
Aurora のアプローチ
Aurora はログだけをストレージに送信することで、この問題を解決しています
In Aurora, the only writes that cross the network are redo log records.
これを可能にしているのは Redo Log の性質です(Section 3.1 より)
Each redo log record consists of the difference between the after-image and the before-image of the page that was modified. A log record can be applied to the before-image of the page to produce its after-image.
つまり
before-image + redo log record = after-image
(変更前) (差分) (変更後)
ログさえあればストレージ側でデータページを再構築できるという仕組みです。だから「THE LOG IS THE DATABASE(ログこそがデータベースである)」というタイトルなんですね〜。
ここから考えられる仮説
文献に直接「クローンの I/O が分離される理由」は書かれていませんでした。以下は、上記の事実から考えた仮説です。
読み取りが影響しないことについて
Aurora のアーキテクチャでは、DB インスタンスとストレージ層は完全に分離されています。DB インスタンスはストレージ層に対して「クライアント」として接続します。
元 DB もクローンも、それぞれ独立したクライアントとしてストレージサービスにリクエストを送ります。Aurora では、ストレージは「サービス」として存在し、各 DB インスタンスからのリクエストを独立して受け付ける設計になっているはずです。
したがって、クローンが共有データを読み取っても、それは「クローンからストレージサービスへのリクエスト」であり、元 DB の I/O パスとは独立していると推察しています。
残る疑問
ただし、物理的に同一のストレージノードに共有データページがある場合、そのノード内での I/O キューやキャッシュはどうなるのか?間接的に影響があるのでは?という疑問は残ります。
この点に関する公開情報は見つけられませんでした。
書き込みが影響しないことについて
「変更を加えると新しいストレージボリュームが作成される」と AWS ドキュメントに明記されていることから、クローンが書き込みを行うとその変更データはクローン専用の領域に保存されます。
また、Aurora では DB インスタンスはそれぞれログをストレージに送信します。元 DB とクローンは別のインスタンスなので、それぞれが送信するログは別々に処理されると考えられます。
ここから、書き込みが発生した後は元 DB とクローン DB は別のボリュームを持ち、I/O も分離されるのではないかと考えています。
まとめ
確定事項
- クローンはコピーオンライトで作成される
- 変更を加えると別のストレージボリュームが作成される
- クローンへの変更はソースに影響しない
- DB インスタンスとストレージ層は分離されている
- Aurora はログのみをストレージに送信する
アーキテクチャからの推論
- 読み取り
- 各 DB インスタンスは独立したクライアントとしてストレージにアクセスするため、I/O パスが分離される
- 書き込み
- 変更後は別ボリュームになるため、I/O が分離される
結論
データの整合性への影響 → 確実に影響なし(文献あり)
I/O 性能への影響 → おそらく影響なし(アーキテクチャから推論)
クリティカルな本番環境で心配な場合は、AWS サポートに確認することをお勧めします。