いますぐ始める高負荷対策
Published in
11 min readMay 31, 2016
こんにちは。松尾です。
今回は、下記の一般的な負荷対策についてご紹介します。
- Webサーバの負荷対策
- データベースサーバの負荷対策
- サーバのスケールアップ
- ネットワークレベルでの負荷対策
- その他の負荷対策
エウレカではPairsとCouplesという2つの高トラフィックなサービスを運営しています。
その中で負荷対策として、さまざまな経験を積んできました。
対策方法を記憶しておくだけで、高トラフィックが襲ってきた際の初動が早くなります。
Webサーバの負荷対策
Webサーバのスケールアウト
①ロードバランサ
- BIG-IP、A10など、商用ロードバランサの導入
- かなりお高いのが悩ましい所です。
- LVS + keepalived で構築する。
- 最近は聞かなくなりましたが、PoundやUltra Monkeyなんかもありましたね。
- NginxやSquidなどWebサーバのリバースプロキシで構築する。
- ELBなど、クラウドサービスのロードバランサを利用する。
②DNSラウンドロビン
- BIND, NSD, unboundなど、DNSアプリケーションサーバで設定する。
- Route53やGoogle Cloud DNSなど、クラウドサービスのDNSサーバで設定する。
③L4 ロードバランサまたはDNSラウンドロビン導入にあたっての注意事項
一つのクライアントが複数のWebサーバにアクセスすることになるので、
ログイン情報などのセッション維持を工夫する必要があります。
主な対策方法は下記。
- LBのスティッキー・セッションを使う
- セッションストレージの利用
- memcachedなどのKVS, MySQLなどのRDBにセッション情報を持つ
- アプリケーションのステートレス化
- 番外編:URLにセッションIDを入れてひき回す
- ガラケー時代の対応
- Cookieや端末IDの仕様がキャリアや端末によってバラバラで統一的にセッションを保持する手段がこれしかなかった時代の手法
- セキュリティ的にアレすぎるので良い子は真似しないように。
その他にも注意点があります。
1. 静的コンテンツをWebサーバ内に動的アップロードしない
- Webアプリケーションの機能としてクライアントからの画像アップロードがある場合、そのクライアントがアクセスしていたWebサーバだけに画像を保存してしまうと色々な問題が起こります。
- NFSを使う手などもあると思いますが、今はS3のようなクラウドストレージを使うのが一番てっとり早いと思います。
2. ロギング
- Webサーバが複数あるので、各サーバにログが保存されているとログを集計・調査するのも一手間ですし、AWSのオートスケールのような仕組みでスケールアウト構成を組んでいるとログが失われてしまうリスクも高いです。
- 以前だとrsyslogやsyslog-ngでリモートでログを管理するケースが多かった。
- 最近だと日本ではFluentd、海外ではLogstashが標準的になりつつあると思います。
④エウレカでの導入事例
- エウレカではAWSのELBを利用してます。
- 現在ブラウザからのアクセスとモバイルからのアクセスでアクセスパターンが、違う為2種類のELBを用意してます。
- ELBはSSLアクセラレータ機能、Cookieによる同一サーバへのアクセス、AutoScaling機能、Closs Zone LoadBalancing等の様々な機能を備えている為とても便利です。
- AWS IOTではWebSocketが使えるので早くELBにも対応してくれると嬉しいです。
Webサーバ(/アプリケーションサーバ)の役割分担
①静的コンテンツの切り出し
- 画像やCSSの読み込み時だけドメインを変えて、別のサーバに配置します。
- Webサーバとアプリケーションサーバを切り分けた時に、静的コンテンツはWebサーバ側で返すようにします。
②CDNの利用
- AkamaiやCloud Frontを使って、エッジキャッシュします。
③画像変換システムの切り出し
- 動的にアップロードされた画像を各端末で最適なサイズ・画質に変換して表示する画像変換のシステムを備えているインターネット・サービスは多いと思います。
- そんな画像変換はそれなりにCPUバウンドな処理で、かつキャッシュしていたりするとI/Oバウンドな処理になります。
- その仕組みを通常のサービスと同一サーバ、同一アプリで処理してしまうと、そこの処理に全体が引っ張られるなんてことになりません。
- なのでエウレカでは、Akamaiを挟んで、nginxのプロキシキャッシュを前段にはさみ、キャッシュがミスヒットした場合のみ裏側のnginxの変換用アプリケーションサーバに流しています。
④エウレカでの導入事例
- エウレカではCDNにAkamaiを使っており、トラフィックのコストが数十万削減できました。
- また現在はEC2上で変換用アプリケーションサーバを構築してますが、運用の観点やコストの観点からAkamaiのImage Managerの利用を検討中です。
- 詳細は別の記事で紹介させていただきます。
データベースサーバの負荷対策
DBサーバのスケールアウト
①レプリケーション遅延
- 1セッション内でマスタにデータを保存してからそのデータを参照する際は、マスタから参照するプログラムにします。
- MySQL接続時にレプリケーション遅延時間を取得し、一定の遅延時間を超えている場合はマスタを参照する形にします。
(例えばその情報をメモリ内に保持しておき、参照用DBコネクション取得のメソッドでその変数を参照。マスタのコネクションを返すか、スレーブのコネクションを返すか判断する)
Tips: MySQL5.6以降だと意図的に遅延を発生させることができる(CHANGE MASTER TOコマンドのMASTER_DELAYオプション)
ので、開発環境でいくらか遅延させておいて動作確認を行うことで遅延処理対策がなされているからをチェック可能です。
②垂直分割
- DBをまたいだ複数のテーブルに対するトランザクションをコミット/ロールバックする必要があります。
- 二層コミットの仕組みを使うなどします。
- 複数のHDD (AWS なら EBS) を利用してTableを保存する領域を分割する事で垂直分割することができます。
- シンボリックリンクにしているだけなので、簡単にIOが分散できます。
③水平分割:sharding
- ハッシングアルゴリズムが重要になります。
- Spiderエンジンなんてものありました。
④Amazon Aurora
- RDSのブラックボックスさとメンテナンス時のフレキシブルさが失われることを許容するのであれば、
- Auroraという選択肢もありだと思います。Pairsでも実験的に導入しています。
⑤エウレカでの導入事例
- エウレカでは現在AuroraやRDSも利用してますが、メインではEC2上にMySQLをインストールして利用してます。
- 以前AWSの方に用途毎にデータベースサーバを構築した方が、いいとアドバイスを頂いたのですが、運用していくと管理コストが、増えると思いそうしませんでした。
- I/Oを分散する施策として複数のEBSをマウントして対応しております。
- 僕個人の意見ですが、id でsharding を行う事は最終手段だと思っており、オンラインデーティングサービスを例にすると、男女で分けるといった事から考えていくと良いです。
DBサーバの適材適所配置
①NoSQL
- レコード数が多くてトランザクションを厳密に管理する必要がないテーブルはDBの垂直分割ではなく、DynamoDBなどのNoSQLなどに負荷を逃がす手もあると思います。
②ビッグデータストレージ
- 通常のRDBMSでは処理に時間・負荷がかかるようなクエリでかつ即応性が求められないものであれば、Amazon RedShiftやGoogle BigQueryなどの分散SQLクエリエンジンや、Treasure Dataなどの導入を検討するべきかと思います。
③エウレカでの導入事例
- エウレカでは現在NoSQLではRedisとDynamoDB、ビッグデータストレージではGoogle BigQueryを導入してます。
- parisではRedisがSPOFになりそうだったので、システムからは利用しない判断をしました。
- DynamoDBについては非常に便利ですがスループットで課金ということもあり、
- 金額が高くなりがちなので使い方について最適化を検討中です。
地道な改善
①データ容量、レコード数を抑える
- データベースの容量が増えるとメモリに乗りきらず、ディスクアクセスが発生する可能性も高まります。
- サービスに必要ないデータはS3などにバックアップをとって消しましょう。
- MySQLだとpartition tableを使うと便利です。
②SQLチューニング
- スロークエリを確認して、地道に改善します。
③ロック競合の解析
- これは負荷対策と言うよりは障害対応時の話ですね。
- ロック競合が発生した際は、そもそもの挿入・更新頻度を減らすという手もありますが、それらがすぐに出来ない場合は地道にSHOW ENGINE INNODB STATUSコマンドで
デッドロックを検出したり、ここに書かれているような手順でロック競合を解析するというがれになるかと思います。
サーバのスケールアップ
①ボトルネックを解決するだけのスペックアップを行う
- CPUバウンドであれば、CPUのコア数、スレッド数、クロック数を上げる。
- メモリバウンドであれば、RAMの容量を上げる。
- I/Oバウンドであればディスクの種類・スペックやファイルシステムの変更、RAID10で負荷を分散したりします。
- ディスクのスペックアップはIOPSを上げる、に尽きると思います。
- SATA,SAS,SSD,ioDriveなどの選択肢
- AWSであればProvisioning IOPSにすることで、IOPSを指定できます。
- ファイル・システムを変更する
- 最近のLinuxであればext4が標準ですが、データベース・サーバだとxfsにするなど
- ネットワーク帯域の問題であれば、AWSであれば純粋にインスタンスタイプを上げる
- 物理サーバであればネットワーク帯域がより広いNICとかを導入する。
- 後は上流のネットワークの帯域(回線、スイッチやロードバランサなどのネットワーク機器の性能)やAWSの場合EC2 — EBS間の帯域も絡んできます。
②ミドルウェアのパラメータチューニング
- ApacheやNginx, MySQLの設定ファイルの各種パラメータのチューニング
- 各種言語の設定ファイルもチューニングが必要ですね。
- ミドルウェア間の通信方法をUNIXドメインソケットに切り替えたり。
③Linuxカーネルパラメータチューニング
- やたらとHTTPリクエスト数が多いサーバ(プロキシ系のサーバ)とかは、
ファイルディスクリプタやTCPネットワーク関連のパラメータのチューニングの
必要性が出てきたりします。
④サーバサイド言語のアクセラレータ
- これはPHPのAPCやeAccelerator的なやつです。
ネットワークレベルでの負荷対策
- ボンディング
- NICの冗長化、負荷分散ができます。ボンディングには複数のmodeが用意されてます。
- ボンディングはNICを複数束ねて使うことで、1個のチャンネルにすることもできます。
その他の負荷対策
- Queue Service
- リアルタイムである必要が無いデータの更新を逐次処理にする方法です。
- 例えば、Pairsではカスタマーのログイン時間を記録しているのですが、
このログイン時間の記録はリアルタイムに行う必要が無いので、
ブートストラップ的な処理で直近のログイン時間が何分か前であれば、
SQSに更新用のキューを入れて、キューを入れたというキャッシュを
記録しています。
まとめ
簡単ではありますが、高負荷に対しての対策手法を書いていきました。
もちろん上記に書いている事はほんの一例です。
結論として一番の対策方法は、地道な改善だと思います。
エウレカでは各APIのレスポンスタイムを可視化したり、
MySQLのSlowLogを可視化して原因となるクエリを改善したりを行っております。
またスモールスタートするにしても、スケールアウトに耐えうるアーキテクチャを
実装する事が大切だと思います。
最低限Webのスケールアウトと、バックアッププランはとても大切な事なので
絶対忘れないようにしましょう。バックアップを取ってリストアするまで検証しておく事で、
安心して運用できるようになります。