ライセンスコストを削れ!
ハードウェアのコストを削る手段は単純です。台数を減らすか、機種のグレードを下げるかです。
一方、ソフトウェアのコストを下げるにはどうすればよいのでしょうか。答えは「合計CPUコア数を減らすこと」です。ソフトウェアのライセンス体系はいろいろありますが、ある程度の規模の商用システムであれば、CPUプロセッサ数に比例するライセンスを採用するのが一般的です。Oracle Databaseのように、CPUコアの種類によって係数を変えるなど細かい計算ロジックはありますが、原則的にCPUコア数が多ければ高く、少なければ安くなります[1]。サーバ台数やメモリサイズ、ディスクサイズなどはライセンスコストに影響しません。
以降では、実際にまかせいのうが実施したコスト削減策を紹介します。
[1] 昔は「サーバ台数に比例する」「CPU数に比例する」といった大雑把なライセンス体系を持つ製品もありましたが、マルチコアプロセッサが普及した現在ではほとんど見ません。
コスト削減策①「アーキテクチャの変更」
PoCに入る前に、改善できる箇所の検討をまず机上で行いました。最も大きな変更は、サーバの物理構成(アーキテクチャ)を見直したことです。
ログ格納機能をサブシステムとして分離しMySQLを採用
当初の設計思想は、すべての機能が1つのデータベースインスタンスで行われるシンプルなものでした。しかし、業務特性をよく分析したところ、業務ログの格納機能は、メイン業務に比べて可用性要件は低いものの、負荷の高い書き込み性能が求められていました。
そこで、ログ格納テーブルを切り出して独立のインスタンスとし、SQL処理の複雑性も低いことからMySQLを採用しました。これにより、メイン業務側の負荷量を下げた上で、RDBMSのライセンスコストも下げることができました。
同時に、大量のINSERT文が発生するログ格納テーブルは、インデックスを全く張らないことで更新処理を高速化しました。また、こまめにバックアップを取得し、古いテーブルデータを削除することで、無駄なハードウェアリソースを使用しない設計としました。業務運用シーンでこのログを参照することはほとんどないと見極めて、思い切ってインデックスを不要と判断したのです。
キャッシュサーバを利用しない
類似システムでは、商用キャッシュサーバ(これもまたライセンスがかかります)を採用していましたが、業務内容からRDBMSのキャッシュチューニングやCDN(Content Delivery Network)の活用で十分と判断しました[2]。

というのも、本案件の主な業務データである教育コンテンツというのは、そう頻繁に更新されるものではないため、ほぼ静的と考えてよいコンテンツだったからです。せいぜい、一ヶ月とか一週間に一回更新される程度でした。念のため類似システムのキャッシュサーバのCPU使用率やネットワーク転送量も調査したのですが、ほぼ使われていない状態だったため、今回はなくても十分に耐えられると判断しました。
[2] CDNはすでに別システムでも導入されており、追加コストは発生しませんでした。
コスト削減策②「テーブル設計の見直し」
物理構成を見直した次に実施したのは、データベースのテーブル設計(ERモデル)の見直しです。テーブルでのデータの持ち方一つで、物理リソースの消費量は大きく変わってきます。
履歴テーブルの全廃
CRUD図を確認したところ、システムには履歴テーブルを参照する機能がありませんでした。そこでお客様と議論し、履歴データをDBに格納しない方針としました。これにより、データ登録時のコストだけでなく、大規模テーブルからの検索や大量レコード削除など、負荷のかかる処理を削減できました。これもまた、DBサーバのCPUリソース削減に大きな効果があります。
参照処理呼び出しのたびに集計しない(サマリテーブルを作成)
参照要求のたびに合計を計算するアンチパターンの設計が見られたため、データ更新時に合計値を算出するように改善しました。
テーブル統合によりI/O量を削減
CRUD図を基に参照処理とテーブルの関係を明らかにし、2つに分かれていたテーブルを1つに統合しました。いわゆる「非正規化」です。これにより、I/O量を削減できました。
SQLによるI/O量が多くなれば、ストレージのI/O帯域だけでなく、CPUリソースもそれだけ多く消費することになります。したがって、性能向上を図る場合、処理対象のデータ量を削減できるかもポイントになります。
コスト削減策③「アプリケーションの修正」
全SQLの机上レビューを実施
すべてのSQL文について机上レビューを行うケースは少ないのですが、今回は性能試験からリリースまでの期間が短いこともあり、机上段階での性能バグ撲滅を目指して、すべてのSQL文をレビューしました。その結果、内部結合で済むところが外部結合になっていたり、インデックスが適切に使われない状態だったりと、多数のバグを発見し修正することができました。
メモリによるキャッシュ管理を最適化
毎回同じ結果を返却するが処理コストの高いSQLについては、結果をキャッシュに保持してSQL発行回数を削減し、CPU処理量を削減しました。また、RDBMSのキャッシュ設計も見直し、頻繁に使用されるテーブルがキャッシュアウトされないようにすることで、性能を安定させました。
アプリ側の無駄な通信を排除
性能試験では、サーバに対する全通信のHTTPパケットをキャプチャして確認を実施しました。その結果、1回の画面遷移で同じURLを複数回呼ぶといった、サーバに余分な負荷をかける不具合が見つかりました。このような処理は、機能的には問題として顕在化しないため、多くの場合は見過ごされがちですが、性能の観点からは無駄にリソースを消費する悪玉です。
このような改善一つ一つは小さいものですが、多くのユーザから同時に処理が実行される負荷時には「チリツモ(塵が積もれば)」となって、大きな効果が期待できます。
PoCを実施した結果
こうした改善を行った後、PoCとして複合性能試験(負荷試験)を実施したところ、目立った性能問題も発生することなく、本番環境のDBサーバのコア数を当初サイジング時の30%に抑えることに成功しました。また、全環境合わせたサーバ費用も当初の50%に抑えられ、概ね当初の企画通りの予算に収めることができました。システム改修やPoCにかかったコストを考慮に入れても、十分にお釣りのくる取引です。
どの業界のお客様でも、競合他社と厳しい競争にさらされています。できる限りサーバ費用(イニシャルコスト+ランニングコスト)を削減することで、トータルで見て競争力のあるシステムを提供できるのです。特に、コスト削減とハイパフォーマンスの両立が求められるプロジェクトでは、サイジングの段階から性能を意識した様々な手段を講じていく必要があります。
こうした1つ1つの活動は地道なものですが、逆にコスト削減には「飛び道具」など存在しないということでもあります。今回は、ハードウェア/ソフトウェアへの投資を抑えるためには、アーキテクチャの精査や業務シーンまで踏み込んだ設計の重要さについてお話しさせていただきました。皆様のお役に立てば幸いです。
