はじめに
こんにちは。スタンバイで求人データ管理に関するバックエンドエンジニアをしている池田です。
スタンバイはWEB上に存在する大量の求人を一括検索できるサービスを提供しており、その求人票のマスタのデータは Amazon Aurora を使って運用しております。 以下の記事で説明をしておりますが、2021年に求人取込直後の求人情報を構造化データとして保存するために Amazon Aurora を採用しました。
- DBエンジンとして
Aurora ( MySQL 5.7 )
を利用しております。 - ストレージエンジンとしてInnoDBを利用しております。
しかし作り直しから時が経ち、求人票の増加や各種機能の追加等によって Aurora のデータ量は想定上の速さで増加していき、それに比例する形でインフラコストも増加し続けていました。
今回反省点とコスト削減のために実施した施策と結果について、紹介します。
増加しているテーブルサイズのグラフ。(1つの線が1つのテーブルのテーブルサイズを示しています)
データ量が増加している要因
一部のテーブルのデータ量が大きく増加し続けている理由は、レコード削除が行われたテーブルに対し、ディスク領域の解放ができていなかったためです。
MySQLやMariaDBではOPTIMIZE TABLE
やALTER TABLE
などのコマンドを実行することで、ディスク領域を解放できます。 1
求人票はかなり頻繁に追加や削除が行われるという特徴があるため、求人データを管理する一連の処理では日常的に大量の INSERT, DELETE が実行されます。
そのため求人票を扱うテーブルのレコード数はあまり変わっていないのに、テーブルサイズはどんどんと増加し続けていました。
データの増加による悪影響
またデータ量が増えるとインフラコスト(storage usage
等)が高くなるだけなく、DBからのデータ取得速度が遅くなってしまい、サービスとしてもレスポンスが遅くなるなどの悪影響が出てしまいます。
- DBからデータを取得する際のインデックススキャン、テーブルスキャンが遅くなります。2
- データの取得に時間がかかるため、サービスのレスポンスが遅くなります。
- データの取得に時間がかかるため、CPU使用率も増加しやすく、リードレプリカを増やす、インスタンスタイプを上げるといった対応が必要になりさらにインフラコストが上昇します。
OPTIMIZE TABLE
を定期的に実行するためには
では単にOPTIMIZE TABLE
を実行すれば良いのかというと、そうではありません。
なぜならばOPTIMIZE TABLE
を実行すると、実行時と終了時に該当のテーブルにメタデータロックがかかるからです。
メタデータロック中に、並行してSelectクエリが実行されるとブロックされます。 3
つまり定期的にOPTIMIZE TABLE
を実行するためには、Aurora のメンテナンス時間を設ける必要があり、
24時間稼働しているWebサービスでは原則として停止時間は設けられないので、Webサービスは直接Auroraに依存しないようにアーキテクチャを変更する必要があります。
当時スタンバイでは、Webサービス上で求人票を表示する際にはこのAuroraを直接参照するアーキテクチャになっていました。
そのため、定期的にOPTIMIZE TABLE
を実行するためには、以下のようにWebサービスからはAuroraに直接依存しないアーキテクチャにする必要があります。
上記はスタンバイのWebサービスへ提供する求人情報をAuroraからではなくDynamoDBを通して提供するアーキテクチャです。 AuroraからDynamoDBへFeederを行うサービスを作り、AuroraとDynamoDB間求人データの同期を行いました。 求人情報APIではDynamoDBの求人データを参照するようにし、Auroraへの依存をなくしました。
アーキテクチャ変更の施策と結果
- Auroraに依存しないアーキテクチャにしたところで、Optimizeをテーブルを実行したところ、データ量が減少しました。4
初回データ削除後のテーブルのサイズ。
定期的なOptimizeの実行後のテーブルのサイズ。
初回Optimize後に定期的にOptimizeを実行することで、未使用領域がshrinkされたりB-TREEインデックスの並びが整理され、結果的にストレージの使用容量が減少しました。
結果として
- インデックススキャン、テーブルフルスキャンが速くなり、クエリーの実行速度が上がりました。
- クエリーの実行速度が向上することで、DBに対してクエリーを実行するアプリケーションの並列度を下げることができるようになり、特にSELECTクエリーの同時実行数が減りました。
- 並列実行されるクエリー数が減ることでDBのCPU使用率も減り、さらにDBのクラスターの台数を減らすことができました。
- 最終的にインフラコストが大きく削減されました。
さらにコストを削減するための施策
スタンバイの求人データの更新頻度を見直して1日の取り込み回数を制限した結果、求人の書き込み数自体が約2分の1ほど減りました。5
この書き込み数を減らす施策によって、Auroraのインフラコストがさらに削減できました。
2023年3月のAurora:StorageIOUsage
と比較して、2023年8月以降には1/3以下に下がりました。
施策と結果について
今回実施した施策をまとめると以下となります。Auroraのインフラコストは施策実施前と比較して約55%削減できました。
- 更新頻度が高いテーブルを定期的にOptimizeできる状態にする。
- 書き込みが多い場合は、書き込みを減らす。
- 不要なトランザクション処理を削除する。トランザクションを使うべきところで使う。
- 書き込みの速度が遅くなり、かつ削除できないログ(InnoDBのログ)がたまり続けるため。
Auroraでのコスト削減の面で一番効果的な施策は、DBへの書き込み、読み込みを減らすことでした。
Aurora:StorageIOUsage
に比例して、コスト全体が 下がっております。
反省点
今回2023年3月と比較して大きくコスト削減ができたのですが、AWS Aurora(MySQL)を採用する際には以下を気にしておくべきであったと痛感しております。
- 設計時にデータ量を正しく見積もる。
- マスターテーブル、トランザクションテーブルでどれくらいのデータ量になるかを見積もる。
- レコードのライフサイクルを考える。
- レコードの削除を多く実行する場合には、どこかのタイミングで
OPTIMIZE TABLE
を実行する必要がある。 - MySQLまたはMariaDBを採用する場合は、定期的に
OPTIMIZE TABLE
できるようなアーキテクチャにする。
- レコードの削除を多く実行する場合には、どこかのタイミングで
- 書き込みが多いアプリケーションの場合は、I/Oレートに比例して料金が上がり続けない料金体系を利用する。
- 過剰なSLOを定めない。
- 今回のケースでは関係者と調整したうえで求人データの更新頻度を1日N回に制限することで書き込み数を減らすことができました。SLO自体が要求・要件に対して過剰になっていないかを確認し、適切なレベルにすることは重要でした。
求人データ管理に関するシステムのリアーキテクチャを進める仲間を募集しています
今回の施策で、求人データの管理に利用しているAuroraの費用は以前よりも半分以下に削減できましたが、まだまだ求人データ管理周りのシステムはコストを削減できる余地があります。 現在はよりcost-effectiveなアーキテクチャを目指して、求人取り込みのリアーキテクチャを進めております。
目標としては更に半分以上にインフラコストを下げたいと思っております。
そのため一緒に求人データ管理周りのシステムのリアーキテクトを進める仲間を募集していますので、少しでも興味があればぜひご連絡ください。
- データストアをAuroraだけなく、DynamoDBやDocumentDBなどを適切に利用することで、よりコストを削減できるアーキテクチャにします。
- 毎日約800万求人の取り込みを行い、大規模なデータ量を扱った開発、保守、運用しております。
- 言語はJava, Goで開発を進めており、チームでGoやストリーム処理の勉強会をしながらみんなで学習しています。
スタンバイのプロダクトや組織について詳しく知りたい方は、気軽にご相談ください。 www.wantedly.com
- 私の Amazon RDS for MySQL DB インスタンスが想定よりも多くのストレージを使用しているのはなぜですか?↩
- 実際にOPTIMIZE TABLEの効果を実測したところインデックススキャンでのデータ取得が、OPTIMIZE TABLEの後に数十秒から0.5秒に改善されました。↩
- AWSのサポートに確認したところ、以下の返答をいただいております。「DDL文の実行にはメタデータロックの取得が必要となる関係上、select文が並行して実行されている場合、ブロックされる動作となります。また、optimize文はInnoDBストレージエンジンの場合、ALTER TABLE ... FORCEという文となり、開始および完了時にごく短時間、メタデータロックを取得する動作となります。」↩
- レコード数が多いテーブルではOptimizeに数日かかり正常に完了しない問題も発生したため、テーブルを作りかえて、データを移行することで対応しました。↩
- AWSには Aurora I/O-Optimizedという I/O 集約型アプリケーションの場合に料金の上昇を抑制できる料金体系があります。 MySQL 5.7ではAurora I/O-Optimizedには対応しておらず、MySQL 8系にすることで、Aurora I/O-Optimizedを利用できるようになります。 今回のAuroraで料金をシミュレーションした結果、料金体系を変更せずに書き込みを減らす案の方がインフラコストを下げられたので、 I/O-Optimizedは利用しませんでした。AWS が Amazon Aurora I/O 最適化をリリース↩