2024年10月アーカイブ

目次


結局、何が違うのか?

こんにちは、アクセルユニバース株式会社、データサイエンティストの世古大樹です。

クラウド環境にデータウェアハウス(DWH)を構築しようとする場合、まず候補に上がるのはAWSの Redshift と、Google Cloud(旧GCP)の BigQuery でしょう。Redshiftが選ばれるのは、主に組織がすでにAWS上にシステムを構築しており、DWHも同じAWS上に作りたいからだと思います。AWSのシェアは国内トップであり(出典1)、「クラウドサービスならAWS」と考える企業も多いです。

一方、データ分析基盤に詳しい人は、Google Cloud のBigQueryを好みがちかもしれません。AWSがクラウドサービス全体をリードしているのに対し、Google Cloud はデータ分析や機械学習の領域に特別な強みがあると知られています。2019年に「DWHにBigQueryを採用しないことは大罪である」と主張するブログ記事(出典2)が公開され、このような意見は今も根強く残っています。

とはいえ、この記事の時からは月日が流れ、データウェアハウスを取り巻く環境は大きく変わりました。上のブログでRedshiftがBigQueryより劣っているとされた理由は「自前のクラスタ管理が必要など、フルマネージドでない」「スケールしない」という運用性の面でしたが、2022年に Redshift Serverless がリリースされ、BigQuery同様のフルマネージド・サーバーレスサービスとして使えるようになりました(今でもBigQueryの長所に「サーバーレス」を挙げる記事は多いですが、Redshiftもとっくにサーバーレスなのです)。

今は、単純に「RedshiftよりBigQueryの方が良い」と言えない状況です。両者の差は縮まっており、ほとんど同じ物のように見えます。RedshiftとBigQueryで共通している主な機能を、筆者が調査できた限りで表にしてみます。1つ1つ読む必要はありませんが、「これほど多くの機能が共通しているならどちらでもいいのでは?」と感じられるのではないでしょうか。

機能ジャンル 機能 備考
処理最適化 自動スケーリング
処理最適化 ストアドプロシージャ
処理最適化 クエリ結果のキャッシュ
処理最適化 マテリアライズドビュー
運用効率化 クエリのスケジューリング
運用効率化 ウェブベースのクエリエディタ Redshift:Redshift クエリエディタ v2
BigQuery:Google Cloud コンソール、BigQuery data canvas
運用効率化 クエリプランの取得 実行中のクエリを細分化したステージや進捗状況を表示する機能
SQLのEXPLAINステートメント
運用効率化 クエリの待ち行列(キュー) Redshift:WLM
BigQuery:クエリキュー
Redshiftはショートクエリを自動で判断して
待ち行列をスキップさせる機能もある。(後述)
運用効率化 手動のテーブル設計・
自動のテーブル最適化
拡張性 フェデレーテッドクエリ・
外部テーブル
DWH外部のストレージや関係データベースにクエリを
かける機能と、そのクエリに必要な便宜上のテーブル
拡張性 半構造化データへのクエリ Redshift:SUPERデータ型
BigQuery:JSONデータ型
拡張性 ユーザー定義関数(UDF) Lambda/Cloud Function を呼び出せるのも同じ。
拡張性 ほぼ全てのBIツールとの円滑な統合
拡張性 SQLでの機械学習モデル構築 Redshift:Redshift ML
BigQuery:BigQuery ML
セキュリティ 保存時暗号化 Redshift:KMSキーでの暗号化(オプション)
BigQuery:Google管理のAESキーでの暗号化(デフォルト)
レジリエンス デフォルトの地理的分散 Redshift:複数AZへの分散
BigQuery:複数ゾーンへの分散
レジリエンス 自動バックアップ Redshift:30分毎の復旧ポイントを0〜35日の設定期間にわたり保持
BigQuery:全時点のタイムトラベルを7日間保持
レジリエンス リージョンレベルの災害復旧 Redshift:スナップショット(手動バックアップ)と
復旧ポイント(自動バックアップ)のクロスリージョンレプリケーション
BigQuery:クロスリージョン・データセットレプリケーション
(プレビュー中の機能)

実際のところ、「どちらを使ってもあまり変わらない」というのも一つの見識だと思います。自社や担当者が詳しい方のクラウドを選択するのも手、です。

しかし、より詳しく見ていくと、両者には明確な違いもあることがわかります。このブログでは、それを 「運用性ならRedshift、コスパならBigQuery」 という視点に整理し、2回に分けて説明します。1回目の今回はRedshiftの運用性を解説します。読者の方が適切なクラウドサービスを選べる助けになれば幸いです。


Redshiftの運用性

Redshiftの運用性が高いと言える根拠は、「1. AWS上の対向システムとの統合容易性」「2. 機械学習ベースの管理タスク自動化」「3. Glue/Lake Formation による一元的なメタデータ・アクセス権限管理」の3つです。


1. AWS上の対向システムとの統合容易性

前述した通り、AWSは国内シェアが高く、クラウドサービス業界を牽引する存在です。そのため多くの組織のITシステムはすでにAWS上に構築されており、オンプレミスでホストされていても、AWSへの移行が検討される場合が多いです。DWHには、これらのITシステムからデータを受け取るための連携が必要なので、DWHも同じAWS上に立てると簡単になります。構成としては、データソース・データレイク・データウェアハウス・データマートをはっきり分けて構築する、以下のようなものが考えられます(あくまで一例です)。

スライド1.jpeg (筆者作成)

Glueが複数種類のデータソースを横断して、簡単にデータ収集・データレイク構築をしてくれるのが見て取れると思います。一方、AWSから Google Cloud へのデータ連携は複雑です。一般的なマルチクラウド構成の複雑さ(クラウド相互の認証認可、学習コスト、運用体制等)はもちろんのこと、RDSから Google Cloud へのデータ転送など、構築自体が大きく複雑になります。

AWS上のシステムと容易に統合できるRedshiftには、 運用簡素性・開発工数・自由度 の面でメリットがあるのです。


2. 機械学習ベースの管理タスク自動化

先ほどRedshiftとBigQueryで共通している機能の表を載せましたが、どちらかにしかない機能もあります。それらの機能の一つ一つは細々としたものですが、「機械学習ベースの管理タスク自動化」とまとめると、塵も積もれば山となる、Redshiftの強みと言って差し支えないものになります。

このことをより具体的に言うと、Redshiftは、 利用しているだけでデータやクエリの状況を自動的に判定し、DWHを最適化してくれる のです。他方のBigQueryは自動最適化の機能が限定的で、BIツールによるクエリを最適化する BI Engine がある程度です。基本的には、モニタリングを行って、その結果を判定し、手動でメンテナンスを行わなければなりません(出典3参照)。Redshiftであれば、機械学習などによって監視・判定・メンテナンスを自動化し、放っておくだけで処理を効率的にしてくれる様々な機能が提供されています。詳しく見ていきましょう。

バラしてしまうと、この節の元ネタは Black Belt(出典4) の、主に pp.20-33 です。この Black Belt 資料自体、Redshiftを理解するうえで必読のものなのでぜひ読んでいただきたいのですが、それはそれとして、この記事では Black Belt をもとにRedshift独自の(=BigQueryにはない)管理機能をまとめ、筆者の観点から補足を行います。


Redshift独自の自動管理機能のまとめ

  • カラム(列データ)の最適化
    • ゾーンマップとソートキー:カラムをあらかじめソートし、ディスクの物理ブロックごとに最小値と最大値を記憶しておくことで、不要なブロックを読み飛ばして処理する。
    • 圧縮エンコードの自動管理:カラムごとに最適なエンコード方式を自動で判定し、圧縮する。(手動選択も可能)
  • テーブルの最適化
    • 自動テーブル最適化:利用状況を判定し、ソートキーや分散キーを必要に応じて自動設定する。(手動設定も可能)
    • Vacuum Delete 自動化:Delete・Update 後の削除領域の開放を、適切なタイミングで自動実行する。(手動実行も可能)
    • ソート自動化:テーブル全体のソートは負荷が高くなるが、機械学習によってソートが必要な部分だけをピンポイントに、適切なタイミングでソートすることで、パフォーマンスやアクセスに影響なくソートできる。(手動ソートも可能)
  • ネットワークの最適化
    • データを均等分散して並列処理に最適化するだけでなく、JOINするレコード同士を同じ場所に置くことで、I/Oを削減できる。
  • クエリ実行の最適化
    • Analyze 自動化:テーブルの統計情報を適切なタイミングで自動更新することで、最適な実行計画のもとにクエリを行える。
    • Auto WLM:ワークロードごとに、物理リソース(メモリ・CPU等)の割当を自動で最適化する。
    • ショートクエリアクセラレーション:クエリの実行時間を予測し、短時間と判断されたクエリは、特別な領域でスムーズに処理される。(オフにもできる)


「ゾーンマップとソートキー」「自動テーブル最適化」について

たとえばテーブルの列 date: DATE がちゃんと日付順に並んでいないと、普通は WHERE date >= '2024-01-01' のようなWHERE句を含むクエリに対してフルスキャンを行う必要があり、応答時間や課金額が無駄に大きくなってしまいます。そこで列をいくつかの塊(1MBのブロックごと)にまとめ、塊の中の最大値と最小値をメモリに保存しておけば、最大値が 2024-01-01 未満である部分を読み飛ばして処理することができ、効率が上がります。これがゾーンマップです。

しかし、これだけではまだ無駄があります。読み込むブロックに含まれる全てのデータが 2024-01-01 以上とは限らないからです。例えば100件中1件のみが 2024-01-01 以上で、99件のスキャンは無駄だった、ということもありえます。そのため、さらに列のデータを降順にソートしておけば、少なくとも最大値が 2024-01-01 以上である最後のブロックの1つ前のブロックまでは全て 2024-01-01 以上ということになるので、効率的な処理が可能です。このようにソートする列を指定するものがソートキーで、適切なソートキーを自動設定してくれるのが自動テーブル最適化です。

スライド2.jpeg (筆者作成)

これらの機能は検索の効率を上げるという点で、インデックスに似ています。BigQueryにはインデックスがあり、一度作成したインデックスを自動管理してくれる機能もあります。しかし、最初にインデックスを作るには手動のDDLコマンドが必要であり、適切なインデックスを作るためにクエリパターンの監視・解析も必要です。管理の簡素さという点で、Redshiftに軍配が上がるところです。


3. Glue/Lake Formation による一元的なメタデータ・アクセス権限管理

これは厳密にはDWHの機能ではありませんが、AWSで多くの場合Redshiftと併用されるサービスのメリットとして挙げます。

「メタデータ」とは何かというと、データレイクやDWHに保存されているデータ(実データ)を説明する情報です。具体的には、テーブル・列・データ型・データソース・データ作成日などの説明です。そしてメタデータは「データカタログ」というデータベースに保管されます。データカタログを調べてメタデータを見れば、どのデータが「使える」データなのか、どの列が何を意味しているのかなどがわかります。DWHに蓄積される膨大なデータを活用するためには、「どこにどんなデータがあるのか」がわかることが重要です(わからないと使えるデータも埋もれてしまいます)。

データに対するアクセス権限管理についても、メタデータが重要な役割を果たします。たとえば、メタデータに「センシティブデータであるかどうか」という列を加え、それを利用してセンシティブデータを閲覧できるユーザーを制限する、というような運用が可能になります。また、(あまり無いことですが)機密情報に関わるメタデータ自体へのアクセスを制限することも考えられます。

AWSではETLとメタデータ・アクセス権限管理が一体化している ため、このあたりの管理が大きく省力化されます。先ほどの構成例をもう一度見てみましょう。

スライド1.jpeg

まず、下の Glue Crawler がデータソースを探索し、メタデータを作って Glue Data Catalog に保管します。保管されたメタデータの使い道は2つあります。1つはGlueのJob(ETLタスク)において、取得対象のデータを特定するために使われます。もう1つは Lake Formation で、テーブルの列単位などのきめ細かい粒度でのアクセス権限を設定するために使われます。Lake Formation のアクセス権限管理はデータレイクだけでなく、データマートにも及びます。Redshiftデータ共有と統合することで、コンシューマーRedshiftのアクセス権限を Lake Formation で管理することができるのです。

要するに、AWSではETLとメタデータがGlueという1つのサービスに合体しており、さらにGlueが統合的なアクセス権限管理を行う Lake Formation と高度に接続されていることで、データの管理負担が抑えられる、ということです。これは大きな特長です。

Google Cloud ではこうは行きません。ETLサービスであるDataflowにはメタデータ管理などの機能がなく、Data Catalog というサービスで別途データカタログを作成する必要があります。また、アクセス権限管理も Data Catalog、Cloud Storage(データレイク)、BigQuery(DWH)で個別に行わなければなりません(かなり大変そうじゃありませんか?)。特に Cloud Storage できめ細かいアクセス権限管理を行うことは、煩雑なため公式に非推奨とされています。Cloud Storage のきめ細かいアクセス権限管理はACL(Amazon S3 のACLと同様のものであり、現在はAWSでも Google Cloud でも非推奨とされている方式)で行う必要があるためです。


きめ細かいアクセスでは、2 つの異なるアクセス制御システム間で調整が必要になります。このため、意図しないデータ漏洩が発生する可能性が高くなり、リソースにアクセスできるユーザーの監査は複雑になります。特に、個人を特定できる情報などの機密データを含むオブジェクトが存在する場合は、均一なバケットレベルのアクセスを有効にしてバケットにデータを保存することをおすすめします。
(出典5)


......と言われても、一部のユーザーから隠さなければならない機密データを含むバケットで均一なアクセスを有効にするとなると、

  1. 機密データを見てはいけないユーザーがそのバケット(=データレイク)全体を見られなくなるか、
  2. 機密データを保管するバケットをデータレイクから分割し、そのバケットにアクセス制限をかけるか


しかありません。1だとそのユーザーのデータ利用が制約されてしまいますし、2だと「すべての生データを1箇所に集約する」というデータレイクのコンセプトそのものに反してしまいます。Google Cloud ではデータレイクのアクセス権限管理に課題があると言わざるを得ないでしょう。


まとめと次回予告

今回の記事では、BigQueryと比較したときのRedshiftの運用性について解説しました。ポイントをまとめておきます。


  1. RedshiftもBigQueryも、多くの機能が共通しているが、詳しく見ると「運用性ならRedshift、コスパならBigQuery」という違いがある。
  2. Redshift中心の構成では、AWS上の対向システムからデータ収集を行うのが簡単。
  3. Redshiftは、自動管理機能によって設定・監視・メンテナンスの負担が小さくなる。
  4. AWSでは、ETL・メタデータ・きめ細かいアクセス権限管理が Glue/Lake Formation に一元化されており、効率的な運用が可能。


次回は、実際にクエリを投げてかかった費用と応答時間を比較しながら、「BigQueryのコスパ」について解説します。「BigQueryはコストパフォーマンスに優れている」とはよく言われますが、必ずしもRedshiftより安くて速い訳ではありません。それでもBigQueryにあるコスパ上の利点とは何でしょうか?(次回記事はこちら


出典(いずれも記事公開時点閲覧)

  1. 総務省『情報通信白書令和6年度版 データ集』「第4章第8節 7. PaaS/IaaS利用者のAWS、Azure、GCP利用率」 https://www.soumu.go.jp/johotsusintokei/whitepaper/ja/r06/html/datashu.html#f00281
  2. データエンジニアの酩酊日記『近年のデータ分析基盤構築における失敗はBigQueryを採用しなかったことに全て起因している』 https://uma66.hateblo.jp/entry/2019/10/17/012049
  3. Google Cloud『BigQuery 管理の概要』 https://cloud.google.com/bigquery/docs/admin-intro?hl=ja
  4. Amazon Web Services『Amazon Redshift 運用管理 AWS Black Belt Online Seminar』 https://pages.awscloud.com/rs/112-TZM-766/images/AWS-Black-Belt2023Amazon-Redshift-Management0331v1.pdf
  5. Google Cloud『アクセス制御の概要』 https://cloud.google.com/storage/docs/access-control?hl=ja




X(旧Twitter)・Facebookで定期的に情報発信しています!

目次


「費用対効果」で選ぶには?

こんにちは。アクセルユニバース株式会社、データサイエンティストの世古大樹です。

今回は、以前のデータウェアハウス比較記事の続編で、BigQuery と Redshift Serverless のコストパフォーマンスを比較する実験を行いました。

詳しくは後述しますが、同じデータセットに対して同じクエリを実行し、かかった料金(コスト)と応答時間(パフォーマンス)を計測しています。

データセットは、80GiB程度の構造化データと360GiB程度の半構造化データの2種類を利用します。それぞれのデータセットに対し、性質の異なる3つのクエリ(合計6つのクエリ)を書きました。クエリ1つにつき5回実行して、特有のオーバーヘッドがある1回目を除外し、2〜5回目の計測結果の平均値を取っています。

「BigQueryの方が安価で性能が良い」とよく言われているようですが、実際に比較した結果を公開しているものは見当たりません。本記事を読めば、コストとパフォーマンスに根拠を持ってデータウェアハウスの選定ができるようになります。


結果:BigQueryは「安定」している

実験の詳細は後回しにして、いきなり結果から発表したいと思います。詳細は .xlsx ファイル(こちらからダウンロード)でご確認いただけますが、ここでは概要の解説を行います。

まず、「コスト」にあたる平均課金額は以下のようになりました。(データセットやクエリの内容は後述します)

スクリーンショット 2024-10-01 15.49.13のコピー3.png

「差」「比」はともに「BigQuery 容量コンピューティング」と「Redshift Serverless」を比較したもので、BigQueryが有利な場合は青色、Redshiftが有利な場合は赤色で表現しています(以降の表も同様)。

「比」を見ると、思いのほか傾向が一定していないのが驚きです。BigQueryの方が安いクエリも、Redshiftの方が安いクエリもあります。クエリ1-1はBigQueryの方が約13倍高い一方、2-2はRedshiftの方が15倍高くなり、ばらつきが大きいようです。

次に、「パフォーマンス」にあたる平均応答時間は以下のようになりました。

スクリーンショット 2024-10-01 15.49.13のコピー2.png

BigQueryの応答時間は比較的安定している(425〜27,212ms、約64倍)のに対し、Redshiftはかなり開きがある(68〜158,411ms、約2300倍)のが目を惹きます。RedshiftのRPU(仮想的な演算リソース)は、BigQueryと近い応答時間になるように設定したのですが、実際に比を1付近にできたのは1-2と2-3だけです。1-1と2-2ではRPUを最低にしてもBigQueryより大幅に速くなり、逆に1-3と2-1ではRPUを非常に大きくしてもあまり速度が上がらず、BigQueryには遠く及びませんでした。

「BigQueryの方がパフォーマンスが高い」とよく言われていますが、むしろ正確には、「BigQueryの方がパフォーマンスが安定している(Redshift Serverless ほど極端に長い応答時間を必要としない)」傾向にありそうだ、ということが読み取れます。

また、平均課金額の表で垣間見えていた、各DWHにとって「どのクエリが得意/不得意か」の傾向は、応答時間にも共通しており、課金額の比が大きい順に平均応答時間の比も大きくなりました。つまり、どちらのDWHにも、「得意なクエリは安いし速い」「苦手なクエリは高いし遅い」という相対的な傾向があるようです。

そこで、各クエリがそれぞれのDWHにとってどの程度「得意」かを表す指標を作ります。平均応答時間の逆数を、クエリに対するDWHの「パフォーマンス」、平均課金額を「コスト」と捉え、パフォーマンスをコストで割ります。この「コストパフォーマンス指数」が高いほど、そのDWHがそのクエリを「得意」としていることを示します。

スクリーンショット 2024-10-01 15.49.13のコピー.png

まず行ごとに「比」を見ると、BigQueryがRedshiftの何倍のコストパフォーマンスを示したかがわかります。全体的にはBigQueryが上回っており、特に2-1では210倍もの差になっていますが、6個中2つのクエリではRedshiftが優勢で、1-1では83倍の差です。必ずBigQueryの方が優れているとは言い切れず、「どちらが良いかは場合による」ことになります。

実験した限りでは、Redshiftが上回った場合の共通点は、「RPUが最低の8でも非常に速いこと(数秒以内)」でした。「CTE(サブクエリ)があるとRedshiftが不利になる傾向にある」ということも言えそうですが、サブクエリはメモリサイズをボトルネックとする処理なので、「メモリサイズと比例するRPUの数を増やしても、サブクエリを含むクエリがあまり速くならないこと」の説明がつきません。事前には、BigQueryではインデックスを張れるのに対しRedshiftではソートキーの指定しか行えないため、JOINが含まれるとBigQueryが大幅に速くなると予想していたのですが、そういった傾向もありませんでした。

困ったことに、BigQueryの測定から得られる情報(スロット数・応答時間・処理データ容量)からは、Redshiftの方が良いのかどうか予想できません。言えるのは、「Redshift Serverless を使っている場合、RPU数が小さくなければBigQueryに移行した方が良くなる見込みが大きい」ということです。

次に列ごとに(縦に)表を見ると、こちらははっきりした傾向が読み取れます。値が大きいほどそのDWHにとって簡単な処理で、逆に小さいほど難しい処理だということになり、最大値と最小値の幅が狭いほど、常に安定したコストパフォーマンスを発揮できることになります。BigQueryの方が、平均課金額・平均応答時間ともに幅の開きが小さかったため、コストパフォーマンス指数も安定しました。BigQueryは 0.00012〜2.4 の20,000倍、Redshiftは 0.0000023〜200 の約90,000,000倍です。「BigQueryのコストパフォーマンスは、Redshift Serverless に比べて、(優れているというより)安定している」というのが、この実験における最も重要な結論です。

平均前の測定値、RPU数、課金額の計算方法などの詳細、および注意事項などは、 .xlsx ファイル(こちらからダウンロード)をご覧ください。


実験条件

以下では、どのような条件の下に実験を行ったのかを、「データセット」「データウェアハウス設定・計測条件」「クエリ」に分けて述べます。ただし、実際に利用したクエリ文等は非常に長いので、末尾の「コード集」にまとめて掲載しました。ここでは日本語ベースで、概要や意図だけを説明します。


データセット

利用したデータセットの1つ目は、Chicago Data Portal の Taxi Trips (2013-2023) です。シカゴ市が公開している、市内のタクシーの賃走についてのデータセットです。11年分の運行について、タクシーID、開始・終了時刻、走行距離、運賃などが記録されています。CSV形式で23列×約2億行、解凍時サイズは約80GiBの、単一テーブルのデータです。表では「小規模構造化データ」と呼んでいます。

2つ目は、Hugging Face にある McAuley-Lab の Amazon-Reviews-2023 です。1996年から2023年までに amazon.com へ投稿された商品レビューが収集されています。レビューデータの集まりである User Reviews、レビューされる商品の情報である Item Metadata の2種類のデータがあり、さらにそれぞれ商品カテゴリでデータセットを分割されています。実験では、商品カテゴリによって分割されたデータを1つにまとめ、種類に応じて2つのテーブルとしました。いずれもJSON形式で、User Reviews は約260GiB、Item Metadata は約90GiB、計360GiBほどです。表では「中規模半構造化データ」と呼んでいます。

いずれも GCS/S3 バケットに保存し、そこから BigQuery/Redshift へロードしました。


データウェアハウス設定・計測条件

今回の実験では、「実際にDWHの利用を開始した時のパフォーマンスを再現すること」を目指し、現実の利用開始時に近い設定としました。

パフォーマンス最適化の基本としては、BigQueryではインデックスの作成、Redshiftではソートキーの指定ができます。DWHの利用が進んでいけば、メトリクスに基づいて最適なインデックスを構築したり、ソートキーが自動的に最適化されたりするところですが、普通、利用開始時にはそこまで行わないと思います。とはいえ、最適化を何も行わないのもリアリティがありません。そこで今回は、「インデックスは主キーにのみ張り、ソートキーにはタイムスタンプ列を指定する」というシンプルな設定にしました。また、RedshiftのVACUUMは、各データベースでデータロード完了後の1度のみ実施しました。

計測に際しては、信頼度の高いデータとするため、リザルトキャッシュの取得を無効化した上で5回連続で同じクエリを行い、2〜5回目の計測結果の平均値を取りました。1回目の結果を除くのは、Redshiftで初回のみコードコンパイルが行われるなど、例外的なオーバーヘッドが発生しうるからです。

リージョン(ロケーション)については、AWS・Google Cloud ともに全て東京単一リージョンで実行し、料金も東京の金額で計算しました。

BigQueryの課金モデルは、予約したスロット(仮想的な演算リソース)の利用量に基づく「容量コンピューティング」と、スキャンしたデータ量に基づく「オンデマンド」の2種類があり、さらに「容量コンピューティング」には3つのエディションがあります。BigQueryの利用を開始する場合、まず「オンデマンド」で自社の利用量等を把握し、「容量コンピューティング」の予約に移行することが想定されていますが、初めから「容量コンピューティング」を使うことも十分あり得ます。そこで今回は、「オンデマンド」を利用してその課金額と処理データ量を記載しつつ、「容量コンピューティング・Standardエディション」の場合のスロット数と課金額も計算し、記録しました(こちらの数値を元に、他のエディションの金額も計算可能です)。

Redshift Serverless の課金額は、RPU(仮想的な演算リソース)の利用量に基づきます。このRPUのベース値は8〜512の間で指定することができ、大きくすると応答時間は短くなりますが、ある程度を超えると短縮効果がコストの増加に見合わなくなります。今回は、原則的にBigQueryの応答時間と近くなるように設定しつつも、コストに見合わないほど大きな値にはしないようにしました。例えばクエリ1-3の応答時間は、RPUが128のとき約160秒でしたが、最大の512に設定しても約100秒と、40%の時短に対して2.5倍のコストになります(コストパフォーマンス指数は0.64倍)。BigQueryは約20秒でしたが、それにできるだけ近づけるのではなく、現実的なラインと考えられた128RPUに設定しました。

なお、最適なRPU数を探索するためのクエリは、測定に影響を及ぼさないように、別のデータベースで行いました。また、Redshift Serverless には1分間の最低課金時間があり、1分未満のクエリであっても1分間分の料金が発生します。しかし実際の利用においては、1分未満のクエリも、他のクエリと同時に実行することで合計1分以上にできると考えられるため、測定されたままのミリ秒数に基づいて料金を計算しました。

以上の料金は、演算(クエリ)に対する費用であり、実際にはストレージ(データ保存)に対する費用も発生しますが、そちらは(通常の利用では)演算に対する費用に比べてかなり安価になることと、デモを行わなくても簡単に見積が取れることから、計測を行いませんでした。また、本記事に掲載されている料金についての情報は公開時点のものであることにご注意ください。料金の詳細について、BigQueryはこちら、Redshiftはこちらをご覧ください。

計測結果の取得には、BigQueryでは後に掲載するクエリを用いました。Redshiftでは、クエリエディタv2のリザルトに表示される Elapsed time で応答時間を計測し、応答時間にRPU数とRPU時間あたりの料金を乗じて課金額を計算しました。RPU数が指定したベースRPU数の通りであることはコンソール上で確認しました。


クエリ

DWHパフォーマンスのボトルネックとして代表的な処理は、ディスクI/OとCPUに負荷のかかるソート(結合・ORDER BY句・ウィンドウ関数)と、メインメモリを消費するサブクエリです。そのため、DWHの主なワークロードである集計(集約関数)を全クエリに含めながら、これらの句や関数の様々な組み合わせを試しました。

できるだけ標準SQLに準拠するようにしました。方言の違いにより、BigQueryとRedshiftで全く同じクエリ文にはなりませんでしたが、同じ処理を実現するものになっています。

また、クエリは集計・分析として意味を持つように作成し、コード集にはその説明も付けておきました。


コード集

前処理

Amazon-Reviews-2023 の Item Metadata は、そのままだと BigQuery の仕様でエラーが出るので、以下のPythonスクリプトで前処理しました。Redshiftの場合は前処理なしで読み込めますが、同一条件で比較するために、同じく前処理したデータをロードしています。



import json
import re

def clean_columns(obj):
    if isinstance(obj, dict):
        keys = list(obj.keys())
        for key in keys:

            # テーブル定義に合わないデータ型を修正する。
            if key == 'rating_number' and not isinstance(obj[key], int):
                del obj[key]
            elif key in ('average_rating', 'price') and not isinstance(obj[key], float):
                del obj[key]
            elif key == 'details' and not isinstance(obj[key], dict):
                del obj[key]
            elif key in ('videos', 'images', 'features', 'description', 'categories', 'bought_together') and not isinstance(obj[key], list):
                obj[key] = []
            elif key == 'images':
                images = obj[key]
                for image in images:
                    for image_size in list(image):
                        if not image_size in ('thumb', 'large', 'variant', 'hi_res'):
                            del image[image_size]
                            print(str(image_size) + 'を削除しました。')

            # BigQueryの仕様に合わせたキーの前処理:使用できない特殊文字を消去し、大文字・小文字の違いを無視する。衝突が発生するか空文字列になる場合は削除する。
            cleaned_key = re.sub(r'[!"$()*,./;?@[\]^`{}~]', '', key.lower())
            if key != cleaned_key or cleaned_key == '':
                if cleaned_key in obj or cleaned_key == '':
                    del obj[key]
                else:
                    obj[cleaned_key] = obj.pop(key)

        for value in obj.values():  # ネストされたオブジェクトに対して再帰的に処理を行う。
            clean_columns(value)
    return obj

def main():
    with open('item_metadata_before_cleaned.jsonl', 'r') as input, open('item_metadata.jsonl', 'w') as output:
        for line in input:
            obj = json.loads(line)
            obj = clean_columns(obj)
            cleaned_line = json.dumps(obj)
            output.write(cleaned_line + '\n')

if __name__ == '__main__':
    main()


データロード等

Taxi Trips (2013-2023), BigQuery

このデータセットのタイムスタンプ形式はBigQueryにサポートされていないので、一度文字列型で読み込んでから変換しています。 また、各データセット共通ですが、BigQueryでは4GB以上のファイルを圧縮形式でロードできないので、解凍したデータを読み込んでいます(Redshiftでは圧縮したまま利用しています)。



CREATE SCHEMA taxi_dataset 
  OPTIONS(
      location='asia-northeast1',
      storage_billing_model='LOGICAL'
  );

CREATE TABLE taxi_dataset.TaxiTrip (
    trip_id STRING,
    taxi_id STRING,
    trip_start_timestamp STRING,
    trip_end_timestamp STRING,
    trip_seconds INT,
    trip_miles FLOAT64,
    pickup_census_tract STRING,
    dropoff_census_tract STRING,
    pickup_community_area INT,
    dropoff_community_area INT,
    fare FLOAT64,
    tips FLOAT64,
    tolls FLOAT64,
    extras FLOAT64,
    trip_total FLOAT64,
    payment_type STRING,
    company STRING,
    pickup_centroid_latitude FLOAT64,
    pickup_centroid_longitude FLOAT64,
    pickup_centroid_location GEOGRAPHY,
    dropoff_centroid_latitude FLOAT64,
    dropoff_centroid_longitude FLOAT64,
    dropoff_centroid_location GEOGRAPHY,
    PRIMARY KEY(trip_id) NOT ENFORCED
);

LOAD DATA INTO taxi_dataset.TaxiTrip
FROM FILES (
    allow_jagged_rows=true,
    field_delimiter=',',
    format='CSV',
    null_marker='',
    skip_leading_rows=1,
    uris=['gs://my-bucket-name/TaxiTrip.csv']
);

ALTER TABLE taxi_dataset.TaxiTrip
    ADD COLUMN new_trip_start_timestamp TIMESTAMP,
    ADD COLUMN new_trip_end_timestamp TIMESTAMP;

UPDATE taxi_dataset.TaxiTrip
    SET
        new_trip_start_timestamp = PARSE_TIMESTAMP('%m/%d/%Y %I:%M:%S %p', trip_start_timestamp),
        new_trip_end_timestamp = PARSE_TIMESTAMP('%m/%d/%Y %I:%M:%S %p', trip_end_timestamp)
    WHERE true;

ALTER TABLE taxi_dataset.TaxiTrip
    DROP COLUMN trip_start_timestamp,
    DROP COLUMN trip_end_timestamp;

ALTER TABLE taxi_dataset.TaxiTrip
    RENAME COLUMN new_trip_start_timestamp TO trip_start_timestamp,
    RENAME COLUMN new_trip_end_timestamp TO trip_end_timestamp;

CREATE SEARCH INDEX tt_pk_index    -- 主キーにインデックスを張る。
    ON taxi_dataset.TaxiTrip(trip_id);


Taxi Trips (2013-2023), Redshift Serverless



CREATE DATABASE taxi_database;

CREATE TABLE IF NOT EXISTS
    taxi_database.public.TaxiTrip (
        trip_id CHAR(40),
        taxi_id CHAR(128),
        trip_start_timestamp TIMESTAMP,
        trip_end_timestamp TIMESTAMP,
        trip_seconds INT,
        trip_miles FLOAT,
        pickup_census_tract VARCHAR(255),
        dropoff_census_tract VARCHAR(255),
        pickup_community_area INT,
        dropoff_community_area INT,
        fare FLOAT,
        tips FLOAT,
        tolls FLOAT,
        extras FLOAT,
        trip_total FLOAT,
        payment_type VARCHAR(64),
        company VARCHAR(255),
        pickup_centroid_latitude FLOAT,
        pickup_centroid_longitude FLOAT,
        pickup_centroid_location GEOMETRY,
        dropoff_centroid_latitude FLOAT,
        dropoff_centroid_longitude FLOAT,
        dropoff_centroid_location GEOMETRY,
        PRIMARY KEY(trip_id)
    )
    BACKUP NO
    COMPOUND SORTKEY(trip_start_timestamp, trip_end_timestamp);    -- タイムスタンプ列をソートキーにする。

COPY taxi_database.public.TaxiTrip (
        trip_id, taxi_id, trip_start_timestamp, trip_end_timestamp, trip_seconds, trip_miles, 
        pickup_census_tract, dropoff_census_tract, pickup_community_area, dropoff_community_area, 
        fare, tips, tolls, extras, trip_total, payment_type, company, 
        pickup_centroid_latitude, pickup_centroid_longitude, pickup_centroid_location, 
        dropoff_centroid_latitude, dropoff_centroid_longitude, dropoff_centroid_location
    )
FROM 's3://my-bucket-name/TaxiTrip.csv.gz'
IAM_ROLE 'my-role-arn'
REGION 'ap-northeast-1'
CSV
DELIMITER ','
NULL AS ''
IGNOREHEADER 1
TIMEFORMAT 'MM/DD/YYYY HH:MI:SS AM'
GZIP;

VACUUM;


Amazon-Reviews-2023, BigQuery



CREATE SCHEMA amazon_reviews_dataset 
    OPTIONS(
        location='asia-northeast1',
        storage_billing_model='LOGICAL'
    );

CREATE TABLE amazon_reviews_dataset.ItemMetadata (
    main_category STRING
    , title STRING
    , average_rating FLOAT64
    , rating_number INT
    , features ARRAY
    , description ARRAY
    , price FLOAT64
    , images ARRAY>
    , videos ARRAY> 
    , store STRING
    , categories ARRAY
    , details JSON
    , parent_asin STRING 
    , bought_together ARRAY
    , PRIMARY KEY(parent_asin) NOT ENFORCED
);

/*
ItemMetadataのみ、スキーマ自動検出でエラーが出るので、スキーマ自動検出をオフにするため、SQLではなく以下のbqコマンドでロードする。

$ bq --location=asia-northeast1 load \
--noautodetect --source_format=NEWLINE_DELIMITED_JSON \
amazon_reviews_dataset.ItemMetadata \
gs://my-bucket-name/item_metadata.jsonl
*/

CREATE SEARCH INDEX im_pk_index    -- 主キーにインデックスを張る。
    ON amazon_reviews_dataset.ItemMetadata(parent_asin);

CREATE TABLE amazon_reviews_dataset.UserReviews (
    rating FLOAT64
    , title STRING
    , text STRING 
    , images ARRAY>
    , asin STRING
    , parent_asin STRING 
    , user_id STRING
    , timestamp INT -- UNIX時刻。
    , verified_purchase BOOLEAN
    , helpful_vote INT
    , PRIMARY KEY(parent_asin, user_id) NOT ENFORCED
    , FOREIGN KEY(parent_asin) REFERENCES amazon_reviews_dataset.ItemMetadata(parent_asin) NOT ENFORCED
);

LOAD DATA INTO amazon_reviews_dataset.UserReviews
FROM FILES (
    format='NEWLINE_DELIMITED_JSON',
    ignore_unknown_values=true,
    uris=['gs://my-bucket-name/user_reviews.jsonl']
);

CREATE SEARCH INDEX ur_pk_index    -- 主キーにインデックスを張る。
    ON amazon_reviews_dataset.UserReviews(parent_asin, user_id);


Amazon-Reviews-2023, Redshift Serverless

Item Metadata の description は本来SUPER型ですが、サイズが大き過ぎてCOPY時にエラーが出るデータがいくつもあります。サイズの限度で打ち切るため、COPY時に TRUNCATECOLUMNS オプションを付け、SUPER型は TRUNCATECOLUMNS の対象にならないため VARCHAR(65535) 型で定義しました。この処理を施してもデータサイズはほぼ変化せず、クエリに description を使うこともないため、測定に問題はありません。



CREATE DATABASE amazon_reviews_database;

CREATE TABLE IF NOT EXISTS
    amazon_reviews_database.public.ItemMetadata (
        main_category VARCHAR(64)
        , title VARCHAR(4096)
        , average_rating FLOAT
        , rating_number INT
        , features SUPER
        , description VARCHAR(65535)
        , price VARCHAR(64)
        , images SUPER
        , videos SUPER
        , store VARCHAR(4096)
        , categories SUPER
        , details SUPER
        , parent_asin VARCHAR(16)
        , bought_together SUPER
        , PRIMARY KEY(parent_asin)
    )
    BACKUP NO;

COPY amazon_reviews_database.public.ItemMetadata (
        main_category, title, average_rating, rating_number, features, description, price,
        images, videos, store, categories, details, parent_asin, bought_together    
    )
FROM 's3://my-bucket-name/item_metadata.jsonl.gz'
IAM_ROLE 'my-role-arn'
REGION 'ap-northeast-1'
JSON 'auto'
NULL AS ''
TRUNCATECOLUMNS
GZIP;

CREATE TABLE IF NOT EXISTS 
    amazon_reviews_database.public.UserReviews (
        rating FLOAT
        , title VARCHAR(4096)
        , text VARCHAR(65535)
        , images SUPER
        , asin VARCHAR(16)
        , parent_asin VARCHAR(16)
        , user_id VARCHAR(128)
        , timestamp BIGINT
        , verified_purchase BOOLEAN
        , helpful_vote INT
        , FOREIGN KEY(parent_asin) REFERENCES ItemMetadata(parent_asin)
    )
    BACKUP NO
    COMPOUND SORTKEY(timestamp);

COPY amazon_reviews_database.public.UserReviews (
        rating, title, text, images, asin,
        parent_asin, user_id, timestamp, verified_purchase, helpful_vote 
    )
FROM 's3://my-bucket-name/user_reviews.jsonl.gz'
IAM_ROLE 'my-role-arn'
REGION 'ap-northeast-1'
JSON 'auto'
NULL AS ''
TRUNCATECOLUMNS
GZIP;

VACUUM;


計測記録の取得(BigQuery)

Redshiftではコンソール上で記録を閲覧するため、コードがありません。



SELECT
    job_id  -- クエリの一意ID。
    , TIMESTAMP_DIFF(end_time, start_time, MILLISECOND) AS elapsed_time_milliseconds  -- 応答時間(ミリ秒)。
    , total_slot_ms / TIMESTAMP_DIFF(end_time, start_time, MILLISECOND) AS num_slot  -- 「容量コンピューティング」の場合のスロット数。
    , 0.051 * total_slot_ms / (1000 * 60 * 60) AS cost_capacity  -- 「容量コンピューティング」の場合の課金額(USD)。
    , total_bytes_billed * POW(2, -40) AS total_terabytes_billed  -- スキャンされたデータ容量(TiB)。
    , 7.5 * total_bytes_billed * POW(2, -40) AS cost_on_demand  -- 「オンデマンド」の場合の課金額(USD)。
    , start_time  -- クエリの開始時刻。
    , state  -- クエリの状態(実行中でないか確認するために出力させる)
    , LEFT(query, 50)  -- クエリ文の冒頭50文字。
FROM `my-project-name`.`region-asia-northeast1`.INFORMATION_SCHEMA.JOBS_BY_PROJECT
ORDER BY start_time DESC;


リザルトキャッシュの無効化(Redshift Serverless)

BigQueryではコンソール上で設定を行うため、コードがありません。



SET enable_result_cache_for_session TO off;  -- 無効化。

SHOW enable_result_cache_for_session;  -- 無効化されていることの確認。


クエリ1-1:小規模構造化データ、単純集計

データセットに含まれる運行の総数と、運賃の平均値を求めます。

BigQuery



SELECT 
    COUNT(*) AS num_trips, 
    AVG(fare) AS avg_fare
FROM taxi_dataset.TaxiTrip;


Redshift



SELECT 
    COUNT(*) AS num_trips, 
    AVG(fare) AS avg_fare
FROM TaxiTrip;


クエリ1-2:小規模構造化データ、集計+ウィンドウ関数

1台のタクシーが1日に乗車された回数のTOP10と、その日付を調べます。


BigQuery



SELECT
    RANK() OVER (ORDER BY COUNT(trip_id) DESC) AS ranking,
    LEFT(taxi_id, 10) AS short_taxi_id,
    CAST(trip_start_timestamp AS DATE) AS date,
    COUNT(trip_id) AS trip_count
FROM taxi_dataset.TaxiTrip
GROUP BY
    CAST(trip_start_timestamp AS DATE), 
    taxi_id
ORDER BY ranking ASC
LIMIT 10;


Redshift



SELECT
    RANK() OVER (ORDER BY COUNT(trip_id) DESC) AS ranking,
    LEFT(taxi_id, 10) AS short_taxi_id,
    CAST(trip_start_timestamp AS DATE) AS date,
    COUNT(trip_id) AS trip_count
FROM TaxiTrip
GROUP BY
    CAST(trip_start_timestamp AS DATE), 
    taxi_id
ORDER BY ranking ASC
LIMIT 10;


クエリ1-3:小規模構造化データ、ウィンドウ関数+CTE+集計

「タクシーは、どの程度スムーズに乗客を見つけられるのか?」を知りたいとします。一つの指標として、「前の乗客を降ろした場所の近くで、すぐに次の乗客を拾うという事象」について調べます。1人目の客を降ろした直後にその近くで2人目の客を拾い、2人目の客を降ろした直後にその近くで3人目の客を拾い......と、この事象は連続することがあります。n回連続した事象を series n trip、n回以上連続した事象を series n or more trip と呼び、n = 1, 2, 3 について相対度数を算出します。


BigQuery



-- タクシーごとに、各運行とその直前の運行の、運行ID、開始/終了座標、開始/終了時刻を取得するCTE。
WITH CurrAndPrevTrips AS (
    SELECT
        trip_id AS curr_trip_id,
        LAG(trip_id, 1) OVER W AS prev_trip_id,
        pickup_centroid_location AS curr_start_point,
        ST_GeogFromText(
            LAG(ST_AsText(dropoff_centroid_location), 1) OVER W    -- 地理データに直接ウィンドウ関数を適用できないRedshiftに合わせた処理(本来BigQueryでは不要)。
        ) AS prev_end_point,
        trip_start_timestamp AS curr_start_time, 
        LAG(trip_end_timestamp, 1) OVER W AS prev_end_time
    FROM taxi_dataset.TaxiTrip
    WHERE
        ST_Distance(pickup_centroid_location, dropoff_centroid_location) 
            < trip_miles * 1609 -- 誤った位置情報を持っている可能性が高いレコードが15%程度あるので、除外する。
    WINDOW W AS (
        PARTITION BY taxi_id
        ORDER BY trip_start_timestamp, trip_end_timestamp, trip_id
    )
),
-- 客を降ろしてから、一定の時間・距離内で、新しい客を拾った運行(series 1 trip)の組を取得するCTE。
Series1Trips AS (
    SELECT
        curr_trip_id,
        prev_trip_id
    FROM CurrAndPrevTrips
    WHERE
        TIMESTAMP_DIFF(curr_start_time, prev_end_time, MINUTE) <= 15
        AND ST_Distance(curr_start_point, prev_end_point) < 500
),
Series2Trips AS (
    SELECT
        Tbl1.curr_trip_id AS curr_trip_id,
        Tbl1.prev_trip_id AS prev1_trip_id,
        Tbl2.prev_trip_id AS prev2_trip_id
    FROM 
        Series1Trips AS Tbl1
        INNER JOIN Series1Trips AS Tbl2
            ON Tbl1.prev_trip_id = Tbl2.curr_trip_id -- 連続している2つのうち前者が、別の連続の後者として存在している場合を捉えて結合する。以下同様。
),
Series3Trips AS (
    SELECT
        S2.curr_trip_id AS curr_trip_id,
        S2.prev1_trip_id AS prev1_trip_id,
        S2.prev2_trip_id AS prev2_trip_id,
        S1.prev_trip_id AS prev3_trip_id
    FROM 
        Series2Trips AS S2
        INNER JOIN Series1Trips AS S1
            ON S2.prev2_trip_id = S1.curr_trip_id
),
Series4Trips AS (
    SELECT S3.curr_trip_id
    FROM 
        Series3Trips AS S3
        INNER JOIN Series1Trips AS S1
            ON S3.prev3_trip_id = S1.curr_trip_id
),
-- 総運行数とn連続運行数を集計するCTE。
DuplicatedCounts AS (
    SELECT 
        (SELECT COUNT(*) FROM CurrAndPrevTrips) AS num_all_trips,
        (SELECT COUNT(*) FROM Series1Trips) AS s1, -- この数は、series 2 trip を重複して2回数えている(一般に series n trip を重複してn回数えている)。
        (SELECT COUNT(*) FROM Series2Trips) AS s2, -- この数は、series 3 trip を重複して2回数えている(一般に series n trip を重複してn-1回数えている)。以下同様。
        (SELECT COUNT(*) FROM Series3Trips) AS s3,
        (SELECT COUNT(*) FROM Series4Trips) AS s4
)
SELECT
    num_all_trips,
    s1 - s2 AS num_series_1_or_more_trips, -- この数は、series n trip を重複なく1回として数えている。以下同様。
    s2 - s3 AS num_series_2_or_more_trips,
    s3 - s4 AS num_series_3_or_more_trips,
    FORMAT('%.2f %%', (s1 - s2) / (num_all_trips * 1.0) * 100) AS rate_series_1_or_more_trips, -- 1.0 を掛けるのは、実数除算を行うための措置。以下同様。
    FORMAT('%.2f %%', (s2 - s3) / (num_all_trips * 1.0) * 100) AS rate_series_2_or_more_trips,
    FORMAT('%.2f %%', (s3 - s4) / (num_all_trips * 1.0) * 100) AS rate_series_3_or_more_trips
FROM DuplicatedCounts;


Redshift



-- タクシーごとに、各運行とその直前の運行の、運行ID、開始/終了座標、開始/終了時刻を取得するCTE。
WITH CurrAndPrevTrips AS (
    SELECT
        trip_id AS curr_trip_id,
        LAG(trip_id, 1) OVER (
            PARTITION BY taxi_id
            ORDER BY trip_start_timestamp, trip_end_timestamp, trip_id
        ) AS prev_trip_id,
        pickup_centroid_location AS curr_start_point,
        ST_GeomFromText(LAG(ST_AsText(dropoff_centroid_location), 1) OVER (  -- Redshift では、地理データに直接ウィンドウ関数を適用できない。
            PARTITION BY taxi_id
            ORDER BY trip_start_timestamp, trip_end_timestamp, trip_id
        )) AS prev_end_point,
        trip_start_timestamp AS curr_start_time, 
        LAG(trip_end_timestamp, 1) OVER (
            PARTITION BY taxi_id
            ORDER BY trip_start_timestamp, trip_end_timestamp, trip_id
        ) AS prev_end_time
    FROM TaxiTrip
    WHERE
        ST_Distance(pickup_centroid_location, dropoff_centroid_location) 
            < trip_miles * 1609 -- 誤った位置情報を持っている可能性が高いレコードが15%程度あるので、除外する。
),
-- 客を降ろしてから、一定の時間・距離内で、新しい客を拾った運行(series 1 trip)の組を取得するCTE。
Series1Trips AS (
    SELECT
        curr_trip_id,
        prev_trip_id
    FROM CurrAndPrevTrips
    WHERE
        (EXTRACT(EPOCH FROM curr_start_time) - EXTRACT(EPOCH FROM prev_end_time)) / 60 <= 15
        AND ST_Distance(curr_start_point, prev_end_point) < 500
),
Series2Trips AS (
    SELECT
        Tbl1.curr_trip_id AS curr_trip_id,
        Tbl1.prev_trip_id AS prev1_trip_id,
        Tbl2.prev_trip_id AS prev2_trip_id
    FROM 
        Series1Trips AS Tbl1
        INNER JOIN Series1Trips AS Tbl2
            ON Tbl1.prev_trip_id = Tbl2.curr_trip_id -- 連続している2つのうち前者が、別の連続の後者として存在している場合を捉えて結合する。以下同様。
),
Series3Trips AS (
    SELECT
        S2.curr_trip_id AS curr_trip_id,
        S2.prev1_trip_id AS prev1_trip_id,
        S2.prev2_trip_id AS prev2_trip_id,
        S1.prev_trip_id AS prev3_trip_id
    FROM 
        Series2Trips AS S2
        INNER JOIN Series1Trips AS S1
            ON S2.prev2_trip_id = S1.curr_trip_id
),
Series4Trips AS (
    SELECT S3.curr_trip_id
    FROM 
        Series3Trips AS S3
        INNER JOIN Series1Trips AS S1
            ON S3.prev3_trip_id = S1.curr_trip_id
),
-- 総運行数とn連続運行数を集計するCTE。
DuplicatedCounts AS (
    SELECT 
        (SELECT COUNT(*) FROM CurrAndPrevTrips) AS num_all_trips,
        (SELECT COUNT(*) FROM Series1Trips) AS s1, -- この数は、series 2 trip を重複して2回数えている(一般に series n trip を重複してn回数えている)。
        (SELECT COUNT(*) FROM Series2Trips) AS s2, -- この数は、series 3 trip を重複して2回数えている(一般に series n trip を重複してn-1回数えている)。以下同様。
        (SELECT COUNT(*) FROM Series3Trips) AS s3,
        (SELECT COUNT(*) FROM Series4Trips) AS s4
)
SELECT
    num_all_trips,
    s1 - s2 AS num_series_1_or_more_trips, -- この数は、series n trip を重複なく1回として数えている。以下同様。
    s2 - s3 AS num_series_2_or_more_trips,
    s3 - s4 AS num_series_3_or_more_trips,
    TO_CHAR((s1 - s2) / (num_all_trips * 1.0) * 100, 'FM999.000') || ' %' AS rate_series_1_or_more_trips, -- 1.0 を掛けるのは、実数除算を行うための措置。以下同様。
    TO_CHAR((s2 - s3) / (num_all_trips * 1.0) * 100, 'FM999.000') || ' %' AS rate_series_2_or_more_trips,
    TO_CHAR((s3 - s4) / (num_all_trips * 1.0) * 100, 'FM999.000') || ' %' AS rate_series_3_or_more_trips
FROM DuplicatedCounts;


クエリ2-1:中規模半構造化データ、CTE+集計

Amazonレビューが「参考になった」を獲得する数に関して分析したいとします。一般に、投稿された直後のレビューは「参考になった」を獲得しておらず、時間の経過とともに獲得数を増やし、十分時間が経過することでそのレビューに相応しい獲得数に漸近すると考えられます。そこで分析の際、「十分時間」が経過していないレビューをWHERE句で排除することで、獲得数が安定したデータのみを対象にできます。そのためにここでは、「十分時間」が何年であるか特定するため、レビューを経過年数の階級に分類し、階級ごとに獲得数の平均値を求めます。そして階級の平均値と全体の平均値の比を取り、その比が一定の閾値に達しているかどうかのフラグを出力させます。

このデータセットのタイムスタンプはミリ秒単位のUNIX時刻です。このデータが収集されたのは2023年9月23日で、UNIX時刻にすると1695427200000になります。


BigQuery



WITH VoteNumWithClass AS (
    SELECT
        helpful_vote
        , CASE
            WHEN timestamp > 1679529600000 THEN '0.0-0.5y'  -- 投稿から経過した時間が0年以上半年未満である階級。以下同様。
            WHEN timestamp > 1663891200000 THEN '0.5-1.0y'
            WHEN timestamp > 1647993600000 THEN '1.0-1.5y'
            WHEN timestamp > 1632355200000 THEN '1.5-2.0y'
            WHEN timestamp > 1616457600000 THEN '2.0-2.5y'
            WHEN timestamp > 1600819200000 THEN '2.5-3.0y'
            WHEN timestamp > 1584921600000 THEN '3.0-3.5y'
            WHEN timestamp > 1569196800000 THEN '3.5-4.0y'
            WHEN timestamp > 1553299200000 THEN '4.0-4.5y'
            WHEN timestamp > 1537660800000 THEN '4.5-5.0y'
            WHEN timestamp <= 1537660800000 THEN '5.0y-'
            ELSE NULL
        END AS class
        , AVG(helpful_vote) OVER () AS overall_avg
    FROM amazon_reviews_dataset.UserReviews
)
SELECT
    class
    , AVG(helpful_vote) / MAX(overall_avg) AS votes_ratio_to_overall
    , CASE
        WHEN AVG(helpful_vote) / MAX(overall_avg) < 0.7
        THEN 'Too recent to rely on.'
        ELSE 'Old enough.'
    END AS eval_usable_or_not
FROM VoteNumWithClass
GROUP BY class
ORDER BY class;


Redshift



WITH VoteNumWithClass AS (
    SELECT
        helpful_vote
        , CASE
            WHEN timestamp > 1679529600000 THEN '0.0-0.5y'  -- 投稿から経過した時間が0年以上半年未満である階級。以下同様。
            WHEN timestamp > 1663891200000 THEN '0.5-1.0y'
            WHEN timestamp > 1647993600000 THEN '1.0-1.5y'
            WHEN timestamp > 1632355200000 THEN '1.5-2.0y'
            WHEN timestamp > 1616457600000 THEN '2.0-2.5y'
            WHEN timestamp > 1600819200000 THEN '2.5-3.0y'
            WHEN timestamp > 1584921600000 THEN '3.0-3.5y'
            WHEN timestamp > 1569196800000 THEN '3.5-4.0y'
            WHEN timestamp > 1553299200000 THEN '4.0-4.5y'
            WHEN timestamp > 1537660800000 THEN '4.5-5.0y'
            WHEN timestamp <= 1537660800000 THEN '5.0y-'
            ELSE NULL
        END AS class
        , AVG(helpful_vote) OVER () AS overall_avg
    FROM UserReviews
)
SELECT
    class
    , AVG(helpful_vote) / MAX(overall_avg) AS votes_ratio_to_overall
    , CASE
        WHEN AVG(helpful_vote) / MAX(overall_avg) < 0.7
        THEN 'Too recent to rely on.'
        ELSE 'Old enough.'
    END AS eval_usable_or_not
FROM VoteNumWithClass
GROUP BY class
ORDER BY class;


クエリ2-2:中規模半構造化データ、JOIN+集計

商品のレビュー数が多くなると、「自分がレビューを書く意味がない」と感じる人が増えて、レビュー数の増加が鈍化する傾向がある、という推測を立てました。これについて調査したいと思います。しかし、単にレビュー数の時間変化率だけを見ると、その商品が人気を失ってレビューされなくなった場合と区別できません。また、サクラを利用していたり、クーポンなどの報酬を用意して購入者にレビューを書くよう促したりしている商品であれば、通常のレビュー数の鈍化パターンに当てはまらないだろうと思われます。

そこでこのクエリでは、「商品ID、商品のレビュー数、商品のレビューについた『参考になった』の総数」の組を取得します。十分なレビュー数がある商品のうち、レビュー数に対して「参考になった」総数が多いものは、鈍化傾向にある可能性が高く、逆に少ないものはサクラなどを利用している可能性が高いと考えられます。

結果セットのサイズが大きいので、Redshiftでは一時テーブルに出力して表示させることができません。UNLOADコマンドによってS3バケットに結果を出力します。


BigQuery



SELECT
    UR.parent_asin
    , MAX(IM.rating_number) AS rating_number -- 1つの parent_asin に対応する rating_number は一意。このMAX関数は形式的なもので、実際は唯一の rating_number が返される。
    , SUM(UR.helpful_vote) AS sum_vote
FROM
    amazon_reviews_dataset.UserReviews UR 
    INNER JOIN amazon_reviews_dataset.ItemMetadata IM
        ON UR.parent_asin = IM.parent_asin
GROUP BY UR.parent_asin;


Redshift



UNLOAD('
    SELECT
        UR.parent_asin
        , MAX(IM.rating_number) AS rating_number -- 1つの parent_asin に対応する rating_number は一意。このMAX関数は形式的なもので、実際は唯一の rating_number が返される。
        , SUM(UR.helpful_vote) AS sum_vote
    FROM
        UserReviews UR 
        INNER JOIN ItemMetadata IM
            ON UR.parent_asin = IM.parent_asin
    GROUP BY UR.parent_asin;
')
TO 's3://my-bucket-name/unload2-2/'
IAM_ROLE 'my-role-arn'


クエリ2-3:中規模半構造化データ、ウィンドウ関数集計+CTE+JOIN

「参考になった」を獲得しやすい、優れたレビューの特徴を分析したいとします。そのためのデータを、2-1で求めた期間の条件でフィルタリングし、適切に加工して取り出す必要があります。しかし、単純に「参考になった」の獲得数を基準とするのは不適切だと考えられます。100件以上の「参考になった」を獲得しているレビューが複数ある商品で10件の「参考になった」を獲得しているレビューと、他に「参考になった」を獲得しているレビューが存在しない商品で10件獲得しているレビューでは、後者をより「優れている」と判定すべきだからです。

そこでこの分析では、パレート分析のような方法を用いることにしました。具体的には、商品ごとに、「参考になった」獲得数の順にレビューを並べ、累積相対度数に応じてグループA、B、Cに分けます(総度数が0の場合のグループはNULLとします)。そしてレビューごとに、そのレビューがどのグループに属するかと、以下の情報をセットにして出力します(後工程で、レビューがどのグループになるかを説明できる変数の組み合わせを探るという想定):「商品ID」、「評価値(星の数)」、「商品全体の平均評価値と評価値の差」、「添付画像の有無」、「レビュータイトルの原文」、「レビュータイトルの単語数」、「レビュー本文の原文」、「レビュー本文の単語数」。

結果セットのサイズが大きいので、一時テーブルに出力して表示させることができません。BigQueryでは別のテーブルを作成した上でそこに結果を保存する設定を行い、RedshiftではUNLOADコマンドによってS3バケットに結果を出力します。


BigQuery



WITH UserReviewWithWindow AS (
    SELECT
        *
        , 1.0 * SUM(UR.helpful_vote) OVER (
            PARTITION BY UR.parent_asin
            ORDER BY UR.helpful_vote DESC
        ) -- 商品ごとに、「参考になった」数の多い順にレビューを並べたときの、「参考になった」の累積度数。1.0を掛けるのは商を浮動小数点値にするため。
            / NULLIF(SUM(UR.helpful_vote) OVER (PARTITION BY UR.parent_asin), 0) -- 商品ごとの「参考になった」合計数。これで割ることで、「参考になった」の累積相対度数になる。
            AS crf
        , RANK() OVER (
            PARTITION BY UR.parent_asin
            ORDER BY UR.helpful_vote DESC
        ) AS rank -- 商品ごとに、「参考になった」数の多い順にレビューを並べたときの順位。
    FROM amazon_reviews_dataset.UserReviews UR
    WHERE UR.timestamp < 1584921600000 -- 2−1で決まったUNIX時刻(3年以上経過)。
)
SELECT
    UR.parent_asin -- 商品ID。
    , CASE -- パレート分析のグループを割り当てる。70%までをAグループ、90%までをBグループ、以降をCグループとする。
        WHEN UR.rank = 1 AND UR.crf IS NOT NULL THEN 'A' -- 相対度数が0.7以上のレビューをAグループに割り振るための処理。
        WHEN UR.crf < 0.7 THEN 'A'
        WHEN UR.crf < 0.9 THEN 'B'
        WHEN UR.crf <= 1.0 THEN 'C'
        ELSE NULL
    END AS pareto_group
    , UR.rating
    , IM.average_rating - UR.rating AS diff_avg_rating
    , CASE
        WHEN ARRAY_LENGTH(UR.images) = 0
        THEN false
        ELSE true
    END AS has_images
    , UR.title AS raw_title
    , LENGTH(UR.title) - LENGTH(REPLACE(UR.title, ' ', '')) + 1 AS num_words_title
    , UR.text AS raw_text
    , LENGTH(UR.text) - LENGTH(REPLACE(UR.text, ' ', '')) + 1 AS num_words_text
FROM
    UserReviewWithWindow UR 
    INNER JOIN amazon_reviews_dataset.ItemMetadata IM 
        ON UR.parent_asin = IM.parent_asin;


Redshift



UNLOAD('
    WITH UserReviewWithWindow AS (
        SELECT
            *
            , 1.0 * SUM(UR.helpful_vote) OVER (
                PARTITION BY UR.parent_asin
                ORDER BY UR.helpful_vote DESC
                ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING  -- Redshift では集約ウィンドウ関数に ORDER BY 句を含める場合、フレーム句が必須。
            ) -- 商品ごとに、「参考になった」数の多い順にレビューを並べたときの、「参考になった」の累積度数。1.0を掛けるのは商を浮動小数点値にするため。
                / NULLIF(SUM(UR.helpful_vote) OVER (PARTITION BY UR.parent_asin), 0) -- 商品ごとの「参考になった」合計数。これで割ることで、「参考になった」の累積相対度数になる。
                AS crf
            , RANK() OVER (
                PARTITION BY UR.parent_asin
                ORDER BY UR.helpful_vote desc
            ) AS rank -- 商品ごとに、「参考になった」数の多い順にレビューを並べたときの順位。
        FROM UserReviews UR
        WHERE timestamp < 1584921600000 -- 2-1で決まったUNIX時刻(3年以上経過)。
    )
    SELECT
        UR.parent_asin -- 商品ID。
        , CASE -- パレート分析のグループを割り当てる。70%までをAグループ、90%までをBグループ、以降をCグループとする。
            WHEN UR.rank = 1 AND UR.crf IS NOT NULL THEN ''A'' -- 相対度数が0.7以上のレビューをAグループに割り振るための処理。
            WHEN UR.crf < 0.7 THEN ''A''
            WHEN UR.crf < 0.9 THEN ''B''
            ELSE ''C''
        END AS pareto_group
        , UR.rating
        , IM.average_rating - UR.rating AS diff_avg_rating
        , CASE
            WHEN GET_ARRAY_LENGTH(UR.images) = 0
            THEN false
            ELSE true
        END AS has_images
        , UR.title AS raw_title
        , LENGTH(UR.title) - LENGTH(REPLACE(UR.title, '' '', '''')) + 1 AS num_words_title
        , UR.text AS raw_text
        , LENGTH(UR.text) - LENGTH(REPLACE(UR.text, '' '', '''')) + 1 AS num_words_text
    FROM
        UserReviewWithWindow UR
        INNER JOIN ItemMetadata IM 
            ON UR.parent_asin = IM.parent_asin;
')
TO 's3://my-bucket-name/unload2-3/'
IAM_ROLE 'my-role-arn'




X(旧Twitter)・Facebookで定期的に情報発信しています!

VMwareからAWSへの移行について

  1. 導入
  2. 移行のステップ
  3. 移行戦略のパターン
  4. 移行後のアーキテクチャ
  5. 移行の成功事例
  6. まとめ


本記事では、VMware環境からAWSへの移行手順・移行の進め方・構成決定方法・成功事例について紹介します。


1. 導入

近年、クラウド移行を検討する企業が増える中で、VMware環境からAWS(Amazon Web Services)への移行が特に注目されています。特に、2024年に実施されたVMwareのライセンス料金改定が移行の大きな動機になっているケースが増加しています。多くの企業が、VMwareのライセンスやサポート料金の上昇に直面し、よりコスト効率の良いAWSへの移行を検討するようになりました。

VMwareからAWSへの移行は、単にコスト削減だけでなく、AWSのスケーラビリティや柔軟性、そしてクラウドネイティブのサービスを最大限に活用できる点でも有利です。本記事では、VMware環境からAWSへ移行するための具体的な手順と、移行後の最適化について詳しく解説します。


2. 移行のステップ

クラウドへの移行は、単にシステムを移すだけではなく、移行後の最適化と運用の効率化が重要です。以下のステップに分けて、移行プロセス全体を進めることが推奨されます。

移行のステップ


2-1. 現状(クラウド移行決定前)評価

移行を検討する際は、まず現状のオンプレミス環境やVMware環境をしっかりと評価する必要があります。この評価では、移行候補となるアプリケーションやシステムがクラウド環境に最適かどうか、またどの移行戦略(Rehost、Replatform、Refactor)が最も適切かを判断します。

特に考慮すべき点は以下の通りです

  • アプリケーションの互換性:AWS上で問題なく動作するかどうか
  • コスト試算:オンプレミスとクラウドのコスト比較
  • セキュリティ要件:クラウド移行後のデータ保護やコンプライアンス対応


2-2. 移行計画

次に、評価結果に基づいて詳細な移行計画を立てます。この段階では、各ステークホルダーとのコミュニケーションが重要です。移行に伴うダウンタイムや、システムの変更点、各チームの役割分担を明確にする必要があります。


2-3. 移行実施と最適化

移行の実施では、計画通りにリフト&シフト、またはリファクター等のアプローチに従ってシステムをAWSに移行します。移行後は、AWSのサービスを最大限に活用し、システムのパフォーマンスやコストの最適化を行うことが重要です。

移行後の最適化には、次のような具体的な作業があります。

  • リソースのモニタリング:AWSのCloudWatchなどを使用して、システムのパフォーマンスやリソース使用率を常時監視し、必要に応じて調整を行います。
  • コスト管理の最適化:AWS Cost ExplorerやTrusted Advisorを活用して、不要なリソースやコスト削減の余地を特定し、運用コストの削減を図ります。
  • セキュリティ強化:AWSのセキュリティツール(AWS Identity and Access Management、AWS Shieldなど)を使い、移行後のデータ保護やアクセス管理を強化します。


3. 移行戦略のパターン

移行を進める前に、まずいくつかの移行戦略を把握する必要があります。

VMwareからAWSへの移行にはいくつかのアプローチがあります。

3-1. Rehost(リホスト、リフト&シフト)

Rehostは、リフト&シフトとして知られ、既存のVMware環境で稼働している仮想マシンをAWS上にそのまま移行する方法です。この方法では、仮想マシン(VM)をほぼそのままAWSのEC2インスタンスに移すため、アプリケーションのコードやアーキテクチャを変更する必要がありません。

メリットとしては、移行が比較的簡単かつ迅速に行えることや、移行中のリスクが低いことが挙げられます。一方で、デメリットとしては、AWSのスケーラビリティやコスト最適化機能を最大限に活用できない場合があるため、クラウドに最適化されていない構成になりやすい点があります。


3-2. Replatform(リプラットフォーム)

Replatformは、「リフト、ティンカー、アンドシフト」とも呼ばれ、既存のアプリケーションをAWSのクラウドサービスに最適化するアプローチです。この方法では、アプリケーションの基本的なアーキテクチャを維持しつつ、データベースをAmazon RDSに移行するなど、一部のサービスやコンポーネントをクラウドネイティブなものに置き換えます。

メリットとして、完全にアプリケーションを再設計する必要がないため、ある程度効率的にクラウド最適化が行え、クラウド特有の利点を享受できます。デメリットは、変更箇所によってはある程度のコストや時間がかかること、特に検証やテストに手間がかかる点です。


3-3. Refactor(リファクター、再アーキテクチャ)

Refactorは、アプリケーションを完全にクラウド向けに再設計する移行アプローチです。アプリケーション全体を再アーキテクチャし、クラウドネイティブな技術(例えばサーバーレスアーキテクチャやマイクロサービス)を導入することで、AWSの柔軟性やコスト効率を最大限に活用します。

メリットは、アプリケーションのパフォーマンス向上やコスト削減、可用性の向上が期待できる点です。また、AWSの豊富なサービスを活用して、アプリケーションのスケーラビリティを高めることができます。デメリットとしては、アプリケーションの再設計や再開発が必要になるため、時間とコストが大幅にかかる可能性がある点です。

移行戦略を検討する際には、以下の観点を考慮して決定する必要があります。

  • Rehostは、最も簡単なアプローチで、迅速に移行を行いたい場合に適していますが、クラウドの特性を最大限に活かすには限界があります。
  • Replatformは、最低限の変更でクラウドのメリットを部分的に享受でき、よりバランスの取れた選択肢です。
  • Refactorは、クラウドネイティブな技術をフル活用できる最も柔軟で効率的な方法ですが、移行の複雑さが増します。


4. 移行後のアーキテクチャ

移行を決めると、どのようなアーキテクチャにするべきか検討します。移行先を選択する際には、大まかに下記のフローで考えることもできます。

移行検討のフロー

図にあるサービスをひとつずつ説明します。

Amazon EC2

ユースケース例: 既存のアプリケーションをほぼそのまま移行し、コスト削減したい

選択理由

  • 最小限の変更で既存のワークロードを移行したい
  • OSのバージョンを変えたくない、特定のOSやソフトウェアバージョンへの依存がある
  • ダウンタイムを最小化して移行したい
  • 段階的なクラウド移行を行いたい


移行ツール: AWS Application Migration Service (AWS MGN)、AWS Backup、VM Import/Export など


Amazon RDS

ユースケース例:データベース管理の負担を軽減したい、コストを最適化したい

選択理由

  • フルマネージドによる運用工数・コストの削減
  • ライセンスコストの最適化
  • データベースエンジンを変更せずに移行したい
  • ダウンタイムを最小化して移行したい


移行ツール: AWS Database Migration Service (AWS DMS)、Oracle Data Pump などのネイティブツール


Amazon WorkSpaces / Horizon + WorkSpaces Core

ユースケース例: 仮想デスクトップ環境 (VDI) を移行したい

選択理由

  • フルマネージドによる運用工数・コストの削減
  • 仮想デスクトップ管理を簡素化
  • コスト効率の高い料金設定
  • Horizon のユーザー体験のまま移行したい(Horizon + WorkSpaces Core)


AWS Outposts Family

ユースケース例: データレジデンシー対応、低レイテンシーが必要な場合

選択理由

  • データの所在地に厳しい規制がある
  • オンプレミスのアプリケーションと低レイテンシーでの接続、リアルタイム処理が必要
  • オンプレミス環境とAWSの一貫した管理を行いたい


移行ツール:AWS Elastic Disaster Recovery (AWS DRS) など


NC2 on AWS / ROSA

ユースケース例:仮想環境の運用体制やオペレーションを極力変えたくない

選択理由

  • 既存環境との親和性
  • 最小限の変更で既存のワークロードを移行したい
  • OSのバージョンを変えたくない、特定のOSやソフトウェアバージョンへの依存がある
  • 段階的なクラウド移行を行いたい


移行ツール:NC2 on AWS: Nutanix Move、ROSA: Migration Toolkit for Virtualization (MTV)


5. 移行の成功事例

5-1.コスト削減とパフォーマンス向上

A社では、VMware環境のライセンスコスト上昇を受けてAWSへの移行を決定しました。リフト&シフト戦略を採用し、既存のアプリケーションをAWS EC2インスタンスに移行しました。結果として、初期移行コストを抑えつつ、AWSのスケーラビリティを活用して、システムパフォーマンスを向上させることに成功しました。


5-2.リファクターで大規模なアーキテクチャ改革

B社は、クラウドネイティブな技術をフルに活用するため、既存のアプリケーションを完全にリファクターし、サーバーレスアーキテクチャに移行しました。移行後、運用効率が大幅に向上し、コスト削減と同時にアプリケーションのパフォーマンスが飛躍的に向上しました。


6. まとめ

VMwareからAWSへの移行は、コスト削減や運用効率の向上、スケーラビリティの獲得といった多くのメリットをもたらします。Rehost、Replatform、Refactorという移行戦略を適切に選び、移行後の最適化を行うことで、AWSの強力なクラウドサービスを最大限に活用することができます。

企業のITインフラを次世代にシフトさせるための一手段として、AWSへの移行をぜひ検討してみてください。




X(旧Twitter)・Facebookで定期的に情報発信しています!

  1. はじめに
  2. AWS移行のためのステップ
  3. 活用できるAWSサービス
  4. AWSとAzureの比較
  5. まとめ


1.はじめに

オンプレミス環境からクラウドに移行することは、企業にとって大きな変革の一歩です。AWS (Amazon Web Services) への移行は、コスト削減、運用効率の向上、スケーラビリティを提供するだけでなく、最新のクラウドテクノロジーを活用してビジネスを加速させる大きな機会となります。しかし、移行プロセスは複雑であり、慎重な計画と適切なツールの活用が不可欠です。本記事では、オンプレミスからAWSに移行する際の具体的な手順、活用すべきAWSサービス、Azureとの比較を含め、移行の成功を導くための実践的なガイドを提供します。


2.AWS移行のためのステップ


移行のためのステップ


移行の計画と準備

オンプレミスからAWSへの移行を成功させるための第一歩は、現状の環境を評価し、適切な移行計画を策定することです。このフェーズでは、移行対象のアプリケーションやインフラ、データを詳細に分析し、それぞれに適した移行戦略を選択する必要があります。多くの企業は「6R戦略」(Rehost、Replatform、Refactor、Retire、Retain、Repurchase)のどれが最適かを判断するために、徹底した環境分析を行います。


事例:製造業の大手企業B社のケース

B社は、オンプレミスのデータセンターをAWSに移行する際、既存のSAPシステムや社内管理ツールを考慮し、移行の計画段階から「Replatform(リプラットフォーム)」を選択しました。特に、同社のシステムは大量のデータを扱っており、パフォーマンスの低下を防ぐために、AWSのデータベースサービスへの最適化が求められました。AWS Database Migration Service (DMS) を使用して、既存のデータベースをAmazon Auroraに移行し、同時にパフォーマンスの改善も実現しています。


移行の実施

移行の実施では、選択した戦略に従って、AWSの各種移行ツールを活用し、データ、アプリケーション、ネットワークの移行を行います。以下に、主なAWSの移行サービスを紹介します。


  • AWS DataSync: オンプレミスのファイルサーバーからAWSのストレージサービス(S3やEFSなど)にデータを移行する際に使います。特に大規模データの転送において、高速かつセキュアな移行が可能です。

  • AWS Application Migration Service (MGN): 物理サーバーや複雑なアプリケーション環境を、最小のダウンタイムでAWSに移行するための強力なツールです。


事例:金融機関C社のケース

金融機関C社は、高度なセキュリティ要件を満たす必要があり、データ移行の際にAWS Snowballを使用しました。大量のデータを安全にAWSデータセンターへ物理的に移行するこのサービスにより、ネットワーク帯域の問題を解決し、機密データの安全な転送を実現しました。結果として、オンプレミスのデータセンターを閉鎖し、すべての運用をAWSの仮想環境に移行できました。


最適化

移行が完了した後は、AWSのクラウドインフラの特性を活かして運用を最適化することが重要です。AWSのCloudWatchを使用して、システムパフォーマンスのモニタリングを行い、リソースの利用状況を監視します。また、AWS Trusted Advisor や AWS Cost Explorer を使用して、コスト効率を定期的に見直し、不要なリソースを削除して運用コストを削減することが推奨されます。


3.活用できるAWSサービス

AWS DataSync

データを迅速に移行するために使用されるAWS DataSyncは、オンプレミスのストレージからAWSに大量のデータを転送する際に非常に効果的です。特に、Amazon S3やEFS、FSx for Windows File ServerなどのAWSストレージサービスとシームレスに連携するため、大規模なファイルシステムの移行に適しています。


AWS Application Migration Service (MGN)

AWS Application Migration Service (MGN)は、オンプレミスの物理サーバーや仮想マシンをリアルタイムに複製し、AWS環境にシームレスに移行するためのサービスです。リアルタイムで変更を反映するため、移行中のデータの整合性を保ち、ダウンタイムをほぼゼロに抑えることができます。


AWS Database Migration Service (DMS)

オンプレミスのデータベースからAWSのRDS、Aurora、DynamoDBなどへデータベースを移行するためのAWS Database Migration Service (DMS)は、最小限のダウンタイムで移行が可能です。特に、異なるデータベースエンジン間の移行にも対応しているため、OracleからAmazon Auroraへの移行など、移行後にコストやパフォーマンスが大幅に改善されます。


事例:EコマースD社のケース

D社は、オンプレミスのSQL ServerからAmazon Auroraに移行する際に、AWS Database Migration Service (DMS)を使用しました。この移行により、既存のデータを失うことなく、ダウンタイムを最小限に抑えたままデータベースの移行が完了しました。さらに、移行後はAmazon Auroraのスケーラビリティとコスト効率の恩恵を受け、大幅なコスト削減とパフォーマンス向上を達成しました。


4.AWSとAzureの比較

クラウド移行を検討する際、AWSとAzureの比較は重要なポイントです。それぞれの強みを理解し、適切な移行先を選択することが求められます。


AWSとAzureの比較


サービスの豊富さ

AWSは、クラウド市場のリーダーとして、最も多くのサービスとリージョンを提供しており、グローバルな企業やスタートアップまで幅広く利用されています。特に、AIや機械学習、サーバーレスアーキテクチャに強みがあります。

Azureは、特にMicrosoft製品との親和性が高く、既にWindows ServerやActive Directoryなどを使用している企業にとっては非常に強力な選択肢です。オンプレミスとクラウドのハイブリッド環境を構築するAzure Stackも大きな特徴です。


ハイブリッドクラウド対応

Azureは、オンプレミスとクラウドを統合するハイブリッドクラウド戦略に強みがあります。特に、Azure Stackにより、クラウドと同じ技術をオンプレミスでも利用できるため、既存のデータセンターとクラウドをシームレスに統合できます。

一方で、AWSも、AWS Outpostsを通じてハイブリッドクラウドをサポートしており、オンプレミス環境でもAWSと同じAPIやツールを使用してインフラを管理できます。


コストとライセンス

AWSは、従量課金制を採用しており、リザーブドインスタンスやSavings Plansによる長期的なコスト最適化が可能です。また、各種の無料利用枠を活用することで、初期コストを抑えることができます。

Azureは、特にMicrosoftの既存ライセンスを持っている場合に有利です。Azure Hybrid Benefitを使用すれば、Windows ServerやSQL Serverのライセンスコストを削減することができるため、特にMicrosoft製品を多く使用している企業には有利な選択肢です。


5.まとめ

オンプレミスからAWSへの移行は、クラウドのスケーラビリティ、柔軟性、コスト効率を最大限に活かすための第一歩です。移行の成功には、適切な計画と移行戦略、そしてAWSの豊富な移行ツールを活用することが重要です。また、AWSとAzureの比較を行い、企業のニーズに最も適したクラウドソリューションを選択することが求められます。

実際の事例を通じてわかるように、AWSの移行ツールを活用することで、大規模データや重要なアプリケーションの移行をスムーズに進め、クラウドへの移行を成功させることが可能です。クラウド移行を検討している企業は、移行後の最適化も含めて、AWSのポテンシャルを最大限に活用し、ビジネスを次のステージへと引き上げることができるでしょう。




X(旧Twitter)・Facebookで定期的に情報発信しています!

  1. はじめに
  2. クラウド運用の課題
  3. 解決方法
  4. クラウド運用の改善事例
  5. まとめ


1.はじめに

クラウドコンピューティングの普及により、多くの企業がオンプレミスのインフラからクラウド環境へと移行しています。しかし、そのメリットを享受する一方で、クラウド運用に伴う新たな課題も浮上しています。特に、コスト管理やセキュリティ、パフォーマンス最適化といった運用面の複雑さが企業にとって大きな問題となっています。本記事では、クラウド運用における主な課題と、それに対する効果的な解決方法を紹介します。


2. クラウド運用の課題


クラウド運用の課題

コスト管理の難しさ

クラウドのメリットの一つに、利用したリソースに応じた課金モデルがあります。しかし、これがかえってコストの見えにくさを引き起こす要因にもなります。特に複数のクラウドサービスを併用するマルチクラウド環境では、どのリソースがどれだけのコストを消費しているのかを把握するのが難しく、意図せぬコスト増加が発生しがちです。


クラウドリソースの無駄遣い

クラウドリソースの無駄遣いとは、使用されていないにもかかわらず停止されていないリソースが増加し、結果的にコストや管理負担が膨らむ状態を指します。これは特に大規模な開発環境や、サービス運用を長期間にわたって行う企業でよく見られる問題です。


セキュリティリスクの増加

クラウド環境に移行することで、セキュリティの責任共有モデルを理解し、オンプレミスとは異なるセキュリティ要件を管理しなければなりません。特に、クラウド上でのデータ保護、アクセス制御、コンプライアンス遵守など、セキュリティリスクに対する意識と対応が重要です。


最適なパフォーマンス維持が難しい

クラウドサービスは多くのメリットを提供しますが、パフォーマンス最適化が難しい場合があります。クラウドサービスプロバイダーが提供するインフラに依存するため、ネットワーク遅延や、キャパシティの問題が発生することがあります。また、アプリケーションの規模が大きくなると、システム全体のパフォーマンス管理が複雑化します。


3.解決方法

解決方法

クラウドコスト管理ツールの活用

クラウドコストを最適に管理するためには、コスト管理ツールの導入が効果的です。AWSのAWS Cost ExplorerやMicrosoft AzureのAzure Cost Management、Google CloudのCost Management Dashboardなどのツールを活用することで、リアルタイムのコスト分析や予算設定、コストの異常な増加を監視できます。これにより、無駄なリソースの特定や、効率的なコスト配分が実現します。


ゼロトラストアーキテクチャの導入

セキュリティリスクを軽減するためには、ゼロトラストモデルの導入が有効です。ゼロトラストアーキテクチャでは、内部外部のネットワークを問わず、すべてのアクセスを検証し、信頼できる通信のみを許可します。また、IAM(Identity and Access Management)ツールを使用し、ユーザーやデバイスのアクセス権を厳密に管理することで、セキュリティを強化できます。クラウドサービスに対して多要素認証(MFA)を導入することも有効な方法です。


リソース起動の自動化とガバナンス

クラウドリソースの無駄遣いを防ぐためには、自動化とガバナンスの強化が必要です。クラウドプロバイダーが提供するツール(例えば、AWSのAWS LambdaAuto Scaling)を活用し、使用していないリソースを自動的に削除またはスケールダウンさせる仕組みを構築しましょう。加えて、運用ポリシーやリソースの使用に関するガバナンスルールを明確に定義し、それに従うことが重要です。


パフォーマンスモニタリングの強化

クラウド上でのパフォーマンスを最適化するためには、モニタリングを強化する必要があります。クラウドサービスプロバイダーの提供するモニタリングツール(AWSのCloudWatch、AzureのAzure Monitorなど)を活用し、システム全体のパフォーマンスをリアルタイムで監視しましょう。これにより、ボトルネックを迅速に特定し、パフォーマンス向上に向けた調整を行うことができます。


インフラ改善・構築の効率化

クラウド環境を効率的に管理し、柔軟にスケールするためには、Infrastructure as Code (IaC)のアプローチが役立ちます。IaCを利用することで、インフラをコードとして管理し、再利用可能なテンプレート化による迅速なデプロイやスケーリングが可能となります。これにより、手動設定によるエラーを減らし、一貫した運用が実現できます。


4.クラウド運用の改善事例

大手Eコマース企業の事例

ある大手Eコマース企業は、急速な成長に伴い、クラウド環境の運用コストが大幅に増加していました。同社は、マルチクラウド戦略を採用していたため、リソースが適切に活用されていないことが大きな課題となっていました。

この企業は、AWS Cost Explorerを活用し、使用されていないリソースやコスト増加の要因を特定しました。その結果、未使用のリソースを削減し、クラウド環境全体でのコスト削減に成功しました。また、Infrastructure as Codeを導入することで、インフラの管理とデプロイ作業を自動化し、運用効率も向上しました。


中堅企業のセキュリティ強化事例

ある中堅企業では、従業員が多様なデバイスからクラウドにアクセスすることが増えたため、セキュリティリスクが懸念されていました。同社はゼロトラストセキュリティモデルを導入し、すべてのアクセスに対して厳格な認証を行う仕組みを構築しました。また、IAMツールと多要素認証を導入することで、セキュリティリスクを軽減しました。これにより安全なクラウド運用が可能になりました。


5.まとめ

クラウド運用には多くのメリットがある一方で、コスト管理やセキュリティ、パフォーマンス最適化など、解決すべき課題も数多く存在します。しかし、適切なツールや戦略を導入することで、これらの課題を効果的に解決し、クラウド環境を最大限に活用することが可能です。企業がクラウドの利点を活かしながら、運用を最適化するためには、継続的な改善と適切な管理体制の構築が不可欠です。

クラウド運用に関するご相談や最適化についての詳しい情報をお求めの場合は、ぜひ私たちにご連絡ください。クラウド導入から運用の最適化まで、貴社のニーズに合わせた最善のソリューションを提案いたします。




X(旧Twitter)・Facebookで定期的に情報発信しています!

  1. 導入
  2. SREにおける視点の違い
  3. SREを外部に委託する利点
  4. SRE外部委託の課題とコストの問題
  5. 自社の一員として動いてくれるSREパートナーを選ぶ
  6. SREを社内で運用するか外部に委託するかの選択
  7. まとめ


SRE(Site Reliability Engineering)の外部委託における利点と課題について、システム運用の効率化とコスト管理の観点で適切な委託先の選び方を解説します。


1. 導入

SREは、システムの安定運用と信頼性向上を実現するために不可欠な役割を果たします。特に、システムの障害発生リスクを最小限に抑え、サービスの継続的な提供を保証することが重要です。しかし、企業がこのSREの役割をどのように担うかという選択は、システムの規模や企業のリソース状況に大きく左右されます。SREを社内で構築し運用するか、それとも外部に委託するかは、企業がシステム運用において直面する大きな課題の一つです。本記事では、SREを外部に委託することの利点と課題、そして成功に導くための適切な委託先の選び方について詳しく説明します。


2. SREにおける視点の違い

SREの基本的な目的は、システムの信頼性を高め、サービスの停止や障害を防ぐことです。これには、システムの自動化、継続的なモニタリング、障害対応体制の強化などが含まれます。技術的には、インフラの冗長化や障害時のフェイルオーバー、負荷分散の最適化などを実施することがSREの重要な業務です。

しかし、ビジネス視点でのSREの役割は、単にシステムを安定的に動作させるだけでは不十分です。企業が成長し、ビジネスの要求が高度になるにつれて、SREの目的は、システムの安定運用をベースに、エンドユーザー体験の向上や業務効率の改善を目指すものに変わります。つまり、技術的なパフォーマンスの最適化だけでなく、最終的にビジネス全体にどう貢献できるかがSREの評価指標となります。SREは、システム運用にとどまらず、企業の戦略的なビジネス目標に向けて、顧客体験を改善し、コスト削減や運用効率化を通じて、企業全体の成長を支える役割も担っているのです。


3. SREを外部に委託する利点

SREを外部に委託する利点

企業がSREを外部に委託する理由はさまざまですが、その主な利点は以下の通りです。


3-1. 専門的な知識と経験の活用

外部のSREチームは、さまざまな業界やプロジェクトで培った専門的な知識と経験を持っています。彼らは、特に大規模で複雑なシステムや、マイクロサービスアーキテクチャを採用している企業において、その知識を活かして迅速かつ効果的に問題解決を図ります。たとえば、オンプレミスからクラウドへの移行や、クラウドネイティブな環境での運用効率化など、社内チームでは得難い専門的なスキルを提供することができます。


3-2. 柔軟な人的リソースの活用

外部委託によって、企業はリソースを柔軟に活用することが可能です。社内でSREチームを固定費として維持するよりも、外部のSREリソースを必要に応じて変動費として活用できるため、コスト管理がしやすくなります。たとえば、繁忙期や新しいプロジェクトの立ち上げ時に追加リソースが必要になった場合、外部のSREチームを一時的に増強することで、短期間での対応が可能となります。


3-3. 高い対応力とスピード

外部のSREチームは、複数のプロジェクトを経験しているため、問題解決や障害対応のスピードに優れています。例えば、突発的なシステム障害が発生した場合、経験豊富な外部のSREチームは迅速に原因を特定し、最適な解決策を実行します。また、24時間365日の対応が求められるような運用環境において、外部委託はスピーディな対応と高い信頼性を提供します。


4. SRE外部委託の課題とコストの問題

しかし、SREの外部委託にはいくつかの課題があります。最も大きな課題はコストの問題です。外部のSREチームを活用する場合、特に長期間にわたるプロジェクトでは、社内でSREを運用する場合と比較してコストが高くなることがあります。契約形態やサービス内容によっては、予算を超過するリスクもあります。そのため、企業はコスト対効果を十分に考慮し、運用効率を維持しつつ、費用を適切に管理する必要があります。

また、外部のSREチームが企業のビジネス戦略や目標を理解しているかどうかも重要なポイントです。単なる技術的な問題解決だけでなく、企業全体の目標にどのように貢献できるかを考える外部チームと連携することが不可欠です。これを実現するには、定期的なコミュニケーションと、目標に対する明確な期待値の設定が必要です。


5. 自社の一員として動いてくれるSREパートナーを選ぶ

SREを外部に委託する際には、単に技術力や価格だけで選ぶのではなく、「自社の一員として動いてくれる」パートナーを選ぶことが重要です。従来のシステム開発においては、顧客からの依頼に基づいて開発会社が対応するという形が一般的でしたが、SREはそれとは異なるアプローチが求められます。

SREの外部委託においては、外部のSREチームが単に顧客の要求に応じるだけでなく、プロアクティブに課題を発掘し、解決策を提案することが重要です。外部チームが率先してシステムの潜在的な問題を見つけ、顧客が気づいていないリスクや改善点を提案することで、システムの安定性やパフォーマンスを向上させることができます。これが、SREを外部に委託する際の真の価値です。

企業は、単なる技術的なサポートを超えて、ビジネス全体に貢献できる外部パートナーを選ぶことが、長期的な成功への鍵となります。このようなパートナーを見つけることで、企業はシステム運用の安定性を確保しつつ、エンドユーザー体験の向上やビジネス価値の最大化を実現できます。


6. SREを社内で運用するか外部に委託するかの選択

SREを社内で運用するか、外部に委託するかの判断は、企業の規模や戦略によって異なります。社内に専任のSREチームを構築することで、特定のプロジェクトや業務に深く関与し、企業のビジネス目標に沿った運用が可能となります。しかし、社内リソースの制約や、システムの複雑化による負荷増加を考慮すると、外部に委託する選択肢も検討する価値があります。

特に、新規サービスの導入や、大規模なシステム更新を控えている場合、外部のSREチームを活用することで、短期間での立ち上げや運用を成功させることができます。企業は、自社の現状と将来的なニーズを踏まえ、最適なSRE戦略を選択することが重要です。

当社では、上記の間をとって、お客様社内へのSREチームの立ち上げを実施することもできます。具体的には、数名のお客様SREチームに経験豊富な当社メンバーも数名参画し、チームの習熟度を高めながら、お客様SREチームがひとり立ちすることを支援しています。


7. まとめ

SREに外部委託、社内チームそれぞれのメリット・デメリット

SREを外部に委託することは、システムの信頼性向上や運用効率化において多くのメリットをもたらします。外部委託による柔軟なリソース活用や、豊富な経験を持つチームによる迅速な対応は、特に大規模なシステム運用において有効です。しかし、外部委託にはコストや適切なパートナー選びといった課題も伴います。

SREの外部委託を成功させるためには、単なる技術的なサポートに留まらず、企業のビジネス目標やエンドユーザー体験にまで貢献できる外部パートナーを選ぶことが不可欠です。SREを自社の成長戦略にどのように組み込むかを明確にし、信頼できるパートナーと連携することで、システム運用の最適化と企業の成長を両立させることが可能です。

SREの外部委託に関するご相談や詳細については、ぜひお問い合わせください。貴社のニーズに合わせた最適なソリューションを提供し、システム運用の成功を支援します。




X(旧Twitter)・Facebookで定期的に情報発信しています!

このアーカイブについて

このページには、2024年10月に書かれた記事が新しい順に公開されています。

前のアーカイブは2024年9月です。

次のアーカイブは2024年12月です。

最近のコンテンツはインデックスページで見られます。過去に書かれたものはアーカイブのページで見られます。