Chapter 1 基礎知識
高速であることとは
- Core Web Vitals
- Googleが検索エンジンのインデックス付けに利用している指標
- 3つの評価軸がある
- LCP (Largest Contentful Paint)
- ページロードのパフォーマンスを計測する指標
- 一番大きなコンテンツが表示されるまでの時間
- 2.5秒以内 にするのが好ましい
- FID (First Input Delay)
- 双方向性を計測する指標
- ユーザーが最初にページを操作してから、ブラウザがその操作に対応した処理を開始するまでの時間
- 100ミリ秒を下回るようにする
- CLS (Cumulative Layout Shift)
- 視覚的な安定性を計測する指標
- 累積レイアウトシフト(一度表示された内容が移動しているかどうか)
- 0.1を下回るようにする
- 他にFCP (ブラウザがコンテンツの描画を開始するまでの時間)、TTFB (操作してからレスポンスの受信を開始するまでの時間)などがある
- 高速なシステム→コスト対性能効率が良い!エネルギー対性能効率も良いのでECO!
高速なWebサービスとは
- 「Webサービス利用者(クライアント・ベンチマーカー)のリクエスト送信開始〜Webサービス利用者のレスポンス受信完了」までの所要時間が短いWebサービス
- このような待機時間をレイテンシ (Latency)と呼ぶ
- 単位は時間で、
msec (ミリ秒=1/1,000秒)
かμsec (マイクロ秒=1/1,000,000秒)
を用いる
- 単位は時間で、
- リクエスト送信開始〜レスポンス受信完了までの一周の所要時間の長さをRTT (Round Trip Time)と呼ぶ
- このような待機時間をレイテンシ (Latency)と呼ぶ
- LCPの2.5秒以内はコンテンツ取得・パース・レンダリングなどを含めた時間であるため、実際のWebサービスとしてRTTは1秒以内に収めるべき
- 同システムで受け付ける多様なリクエストを俯瞰して、多様なリクエストを高速に処理することが求められる
- 同時並行で行われる大量のリクエストを少ないシステムリソースで処理することが求められる
- この同時並行処理性能をスループットと呼び、単位は単位時間あたりのリクエスト処理数
rps (request per second)
- 同時接続数と呼ばれることもある
- この同時並行処理性能をスループットと呼び、単位は単位時間あたりのリクエスト処理数
- Webサービスを高速化するにはその構造を理解する、構造には論理的な構造と物理的な構造がある
- 論理的な構造
- Webサービスの動作や仕組み
- いわゆるソフトウェアアーキテクチャ
- 物理的な構造
- いわゆるインフラストラクチャ
- 論理的な構造
Webサービスの負荷
- 高負荷な状態とは、Webサービスを提供するシステムリソースのうち、短時間で利用料が大きく変わる、時間流動性が高いシステムリソースの利用率が高い状態
- 代表的なシステムリソースとして、CPU時間・メモリ領域・メモリI/O帯域・ネットワークI/O帯域・ストレージI/O帯域がある
- キャパシティ
- リクエストの文脈では、処理可能なスループットや同時接続数
- システムリソースの文脈では利用可能なリソース量
キャパシティの見積もり
- Webサービスのキャパシティは、その時に利用可能な計算資源の量となる
個々の性能×数
で表せる
- キャパシティを調整するアプローチは2種類ある
- 垂直スケーリング: 個々の性能を変える
- 水平スケーリング: 数を変える
- 必要十分なキャパシティとは、需要に対して不足せず・過剰でない量
- 必要十分なキャパシティが供給されていると、レイテンシとスループットは維持され、エラーにならず、目に余る余剰がない
- 安定的な指標が出ているのであれば必要十分もしくは余剰である可能性が高い
- キャパシティ供給が不足すると、レイテンシ増加・接続不可・データ破壊など、ひどい状況になる
- キャパシティが余剰だと無駄なお金を払うことになる
- 必要十分なキャパシティが供給されていると、レイテンシとスループットは維持され、エラーにならず、目に余る余剰がない
- キャパシティの需給をバランスさせるためには、需要側の調整、供給側の調整のどちらもがある
- 需要側の調整
- キューイング: 処理要求を順番待ちさせる
- レートリミット: 単位時間あたりの処理要求の受け付けに上限を設ける
- ランダマイズ: 処理要求の発生にランダムな待ちを潜ませ、タイミングを分散させる
- 技術以外の手法
- 先着順を辞めて抽選にする
- チケットを時間差で配る
- 供給側の調整
- ユーザーに影響なく実施できるので、対応の軸になることが多い
- スケーリング
- 従来は事前に負荷を予測してスケーリングするプロアクティブな手法が用いられてきた
- クラウド全盛になりオートスケーリングを用いてイラスティックにスケーリングできるようになった
- ただし、以下のような課題がある
- システムリソース需要の変化とスケール完了までの間にリードタイム(時差)がある(リードタイムによってユーザーを待たせてしまう)
- インフラ基板側のキャパシティが売り切れており、スケーリングできない
- ただし、以下のような課題がある
- そのため、プロアクティブ+リアクティブな対応が必要
- 需要側の調整
- 必要なキャパシティの見積もり
- 原則として事前予測は不可能
- 「試す」アプローチが必要
- 負荷試験を行う(ただし、必要十分量の保証は不可能)
- 土台となるデータが取得できる
- 原則として事前予測は不可能
パフォーマンスチューニングの基本
- 原則
- いきなり手を動かさない
- 勘で行動しない
- 考えて行動する
- 推測せず計測する
- 負荷試験などでは被負荷側だけでなく与負荷側のモニタリングもすべき
- 高速になったという結果もならなかったという結果も等しく重要
- 公平に比較する
- 比較するときは前提条件を揃える
- 複数回実施する(1セットあたり3~5回)
- 1つずつ比較する
- 同時に複数の項目に手をつけない
- どれが効いたかわからなくなる
- 同時に複数の項目に手をつけない
- 制約理論
- 一番にボトルネックとなっている箇所のスループットが全体のスループットになるという考え方
- ボトルネックにだけアプローチする
- ボトルネック以外に手を出しても制約理論からスループットは変わらない
- ボトルネックの特定は外側から順番にやる
- まずそれぞれの要素の入口と出口で所要時間を計測し、システムリソースの利用状況を確認し、ボトルネックを見つける
- システムリソース上限の問題がある場合は特徴的な時系列推移になることがある
- 現代のWebサービスにおいてボトルネックになりがちな箇所はCPU、メモリ、ディスクI/O、ネットワークI/Oである
- システムリソース上限の問題がある場合は特徴的な時系列推移になることがある
- まずそれぞれの要素の入口と出口で所要時間を計測し、システムリソースの利用状況を確認し、ボトルネックを見つける
- ボトルネックの対処には3つの基本パターンがある
- 解決: 課題になっている事象を根本から解決する
- 該当箇所がボトルネックでなくなるよう処理方法を変更する
- 速いWebアプリケーションを書き直す
- 回避: 課題になっている事象がボトルネックにならないよう迂回・省略する
- 構造や仕組みを変更し、処理そのものを不要にする
- 処理結果をキャッシュし使い回す
- 緩和: 課題になっている事象の影響を和らげる
- 配置変更、設定変更、スケールアウト、スケールアップなどを行い、ボトルネックの程度を緩和する
- 解決: 課題になっている事象を根本から解決する
- 解決が望ましいが、回避した場合のほうがコストは小さい→解決も回避もできない場合は緩和を狙う
- 対応した結果該当の箇所がボトルネックでなくなった場合、その要素を更に高速化するより別のところに手を付けるべき
- パフォーマンスチューニングの具体的な活動は、負荷試験→改善→負荷試験のループ
- 負荷試験計画
- 実施準備
- 負荷施行→結果確認→改善→負荷施行→結果確認→改善…
- 負荷試験計画では目的、手段、目標を決めるのが重要
- 目的が最重要要素
- 負荷施行・結果確認フェーズのポイント
- 負荷をかけながら手動でも利用して使用感を確かめてみる
- 実施時間・実施結果・メトリクス・ログをセットで自動的に記録しておく
- 実施結果の内容を都度解釈する
- パフォーマンス: X並列でYユーザーがN分間で操作完了
- 異常の有無: エラーレスポンス、システムエラー、不審な挙動、不安定なレスポンスタイム
- ボトルネックの移動
- 値が想定通りに変化したか
- 与負荷側のメトリクスも同時に確認する
- 与負荷側がボトルネックになっており、十分な負荷がかけれていない場合もある
Chapter 2 モニタリング
- サービスをモニタリングすることは、高速であることを保証し続けること
- モニタリングは一貫した変わらない視点でも続けることが重要
- 公平に比較する原則
- 目的を確実に定める
- モニタリングは外形監視と内部監視に大別することができる
- 外形監視
- 動作しているアプリケーションの外側からモニタリングする手法
- ユーザーと同じ経路からサービスを利用し、動作を確かめることが主な目的
- Synthetic Monitoringとも呼ばれる
- なるべくユーザーの近くからモニタリングを行う
- よりユーザー体験に近づけるため、シナリオテストを行う場合もある
- 内部監視
- 動作しているアプリケーションの内側からモニタリングする手法
- ユーザーが見えない部分の状態をモニタリングする
- メトリクスを取得し、リソースが過不足なく存在しているかなどを確認する
- サービスが動作している環境にて、エージェントと呼ばれるモニタリング用のデーモンを用いてメトリクスを取得する
手動でのモニタリング
top
- CPU使用率を確認できる
free
- メモリ使用量を確認できる
stress
- CPUに負荷をかけ、サーバーの性能を試験できる
vmstat
- リソース全体の概要が見れる
自動モニタリング・モニタリングツール
- メトリクスを自動で収集し、保存する
- 保存したメトリクスをWebブラウザなどで時系列順に表示する
- 集計用のクエリなどで表示を切り替えられる
- メトリクスが特定の閾値に達すると通知を行う
モニタリングツールのアーキテクチャ
- Pull型とPush型の2つに大別できる
- 内部監視をする場合にはどちらの環境においてもエージェントを常駐させる
- Pull型
- モニタリングアプリケーションがエージェントへメトリクスを要求するアーキテクチャ
- エージェントはリクエストを受け取った時のみメトリクスを収集する
- メリット
- メトリクスの取得間隔をモニタリングアプリケーションで管理できる
- エージェントの実装をシンプルにできる
- Push型
- エージェントがモニタリングアプリケーションへメトリクスを送信するアーキテクチャ
- 1分おきなど、所定のタイミングでメトリクスを収集する
- メリット
- エージェントが動作しているサーバにおいて、ポートに対する接続を許可する必要がない(むやみに穴を開けなくて良い)
- モニタリングアプリケーションの設定を変更する必要なく、モニタリング対象の増減が可能になる
- この特性により、モニタリング対象数やそのリソースの変動が激しい環境(サーバーレス環境など)では、Push型のほうが管理が楽になる
- 収集したメトリクスはディスク容量を圧迫するため、目的を持って収集し、不要なメトリクスは集めないでおくべき
- 何を対象としてモニタリングを行うかであり、何を用いてモニタリングを行うかは大きな問題ではない
- このようなアプリケーションにおけるリソースに対するモニタリングをAPM(Application Performance Management)と呼ぶ
モニタリングの注意点
- グラフ上での変動に惑わされず、実際に値としてどのくらい変動したかを見る
- 縦軸横軸両方の上限値・下限値を確認しておく
- 多くの場合、縦軸を固定して表示すると良い
- 収集したデータに対してどのような加工が行われているかに気をつける
- 例えば
rate()
関数は瞬間的な値の変化を丸めてしまい、グラフとして現れにくい等といった特徴を把握しておく
- 例えば
- 2つのグラフを比較するときは他の条件を合わせる
- 異なる原点を定めたグラフを比較しないようにするため、絶対値でグラフに描き条件を合わせる
- モニタリングアプリケーションやエージェントもシステムリソースを消費して動いている
- 特にエージェントはWebアプリケーション側で動いているので注意
- 負荷試験のために発生する負荷を考慮する
- 負荷試験を行う際に、Webアプリケーション・エージェントと与負荷側を分離する
- 与負荷側はリクエストを多く送るという性質上多くのリソースを使用するので、それによる負荷がアプリケーション側に影響しないようにする
- 負荷試験を行う環境は本番環境で運用する環境になるべく近づける
- RFC 2544 - Benchmarking Methodology for Network Interconnect Devices参照
- 負荷試験を行う際に、Webアプリケーション・エージェントと与負荷側を分離する
- モニタリングの間隔に注意する
- 高負荷な状況が続いていた時間に比べて、モニタリングの間隔が長いと、その状況が上手く補足できない→モニタリングの解像度が低い・足りていない状態
- できるだけリアルタイムに解像度が高くなるようにしておく
- ただし、モニタリングの性質を考慮する必要がある
- ベンチマークなどでは間隔を短く、運用監視など長期的に取得する場合は長くするなど
- ただし、モニタリングの性質を考慮する必要がある
Observability(可観測性)
- アプリケーション内部でどのようにリソースが利用されているかを調査するツールとしてプロファイラがある
- 特にソースコードのどの行にどの程度時間がかかっているかを可視化するツールをラインプロファイラと呼ぶ
- WebアプリケーションにおけるCPU利用率が増えている場合は、ラインプロファイラを用いてどの関数が特にCPU時間を利用しているかを確かめる手法を採る
- ラインプロファイラによって集められた、それぞれの関数におけるCPU時間を階層上に積み上げたグラフとして可視化するツールをFlame Graphと呼ぶ
- 特にソースコードのどの行にどの程度時間がかかっているかを可視化するツールをラインプロファイラと呼ぶ
- Microservicesアーキテクチャでは複数のWebアプリケーションでサービスを提供するため、1リクエストに複数のWebアプリケーションが介することとなり、かかったリソースを捉えることができない
- この問題を解決するために分散トレーシングと呼ばれる手法が提案されている
- 分散トレーシングを含めたTracing、Logging、Metricsの3つを主要素としたモニタリングの考え方をObservability(可観測性)と呼ぶ
ログによるモニタリング
- Webアプリケーションが出力するログを用いてモニタリングを行うことができる
- 例えば一定時間ごとのアクセスログを集計することで、どの時間帯にどの程度のアクセスがあるかをモニタリングできる
- エラーログをモニタリングし、アラートを出すといったことも考えられる
- ERRORログは発生次第すぐ対応、WARNは1分に10件以上発生していれば対応などといった施策ができる
Chapter 3 基礎的な負荷試験
nginx
access_log /path/to/conf [compression];
[compression]
忘れると該当のログフォーマットが適用されない
ab
(Apache Bench)- apache2-utilsにあるベンチマーカー
- 単一のエンドポイントに対して指定の条件分リクエストを送る
- UXを考慮すると、10ms程度の違いは分からない
top
top
コマンドのid
を見ることで、CPU全体のうち何割を使用しているかわかる30 id
なら100 - 30 = 70
で全体の7割を使用していることになる
- 逆に各プロセスの使用率を表すテーブルでは、1コアをすべて使った場合を100とした使用率を表示している
- 2コアをすべて使った場合は200%になる
top
コマンド実行中に「1」キーを押すと個別のCPU使用率が見れる
mysql
- スロークエリログの出力設定
/etc/mysql/mysql.conf.d/mysqld.conf
- ここの
slow_query_log
等々を有効化する long_query_time
で何秒以上掛かったクエリのログを出力するかを設定できる
- ここの
- Webサービスでは実行に長い実行時間のクエリも問題になるが、例え1回の実行時間が短くとも、それが大量に行われている場合も問題になる
long_query_time
を設定しても、そのような1回が高速なクエリはログに出てこないので注意する必要がある- パフォーマンスチューニングでは0にするのが推奨されている
mysqldumpslow
でスロークエリログをいい感じに見やすくできる- デフォルトでは、ログ中の実行時間の合計が長いクエリ順に表示してくれる
- ログを消したり名前を変えたりしたときは、
mysqladmin flush-logs
で知らせる
- スロークエリログの出力設定
- 1つの変更ごとに負荷試験を行うことが重要
dstat —cpu
- 時系列でCPU使用率を見れる
- キャパシティプランニングでは、CPU使用率が50%を超えないようにするのが良い
Chapter 4 シナリオを持った負荷試験
k6
- Grafanaが作ってる負荷試験ツール
- JavaScriptでシナリオが書けるが、実行エンジンはGo
—vus
- Virtual Users
- 並列度
ab
の-c
—duration
- 何秒間実行するか
ab
の-t
http_reqs
- Request per second
- 1秒間に処理できたリクエスト数
http_req_duration
のavg- Time per request
- レイテンシ
- 実運用では負荷走行時間は最低5分は必要
Chapter 5 データベースのチューニング
pt-query-digest
- いい感じにスロークエリログの分析をしてくれる
インデックスによる高速化
EXPLAIN
rows (examined_rows)
- データを選択するために読む必要がありそうと判断された行数
Extra
- 追加情報としてオプティマイザがどのような方法を選択したかが書かれていたりする
Using Where
- インデックスがないカラムでwhereで条件検索している
- 対象になりそうな行を全部見ているので、examined_rowsが大きくなる傾向
- 基本インデックスを貼ることを考慮して良い
Using Index
- インデックスのみで解決できる
- インデックスが効いている状態
- インデックスのみで解決できる
Using filesort
- order-by句においてインデックスが適用できず、ソートが行われている状態
- 多く使用されるクエリならインデックスを貼ったほうが良いかも
- order-by句においてインデックスが適用できず、ソートが行われている状態
Using index condition
- 部分的にインデックスが適用できる状態
- ICPが使用されている時にも表示されるらしい
Using temporary
- 中間テーブルが使用されている状態
- 中間テーブルにはインデックスが効かない(作られていない)のでソートされる分遅くなる
Using index for group-by
- group-byしてるクエリで集約関数を使った時に、インデックスだけで集約できた状態
- プライマリインデックス
- 全てがユニークである、テーブルを代表するデータに貼られるインデックス
- 主キーに自動的に付くインデックス
- クラスターインデックス
- プライマリインデックスの先に実際のデータも含まれていること
- プライマリインデックスを参照した時点で対象のデータが取れるので、ストレージに対してのアクセス数が減る
- セカンダリインデックス
- プライマリインデックス以外のインデックス
- インデックスの先にはプライマリインデックスが含まれている
- そのため、セカンダリインデックスを参照した時には以下のようになる
- セカンダリインデックスで検索
- 見つかったidを元にプライマリインデックスで検索
- クラスターインデックスにより検索結果には実データが含まれているのでそれを結果とする
- セカンダリインデックスのみで結果が分かるときはプライマリインデックスへのアクセスを行わない
- これをカバリングインデックス(Covering Index)と呼ぶ
- Extraに
Using Index
と出てる場合はインデックスだけで解決できることを示す
- インデックスの追加・更新はオーバーヘッドがあるので、インデックスが多すぎる場合には負荷が高まる
- MySQLではクエリ実行時に1つのテーブルに対して選択されるインデックスは1つなので、複数インデックスを貼っても意味がない
- 様々な条件で検索する機能がある場合、頻繁に使用されるものはインデックスを貼り、それ以外は「ORDER BY狙いのインデックス」を作成すると良い
- あるテーブルに対する検索条件の並び順が日付順の場合、新しいデータのほうが頻繁に参照されそうなので、インデックスを日付の降順に貼ることで最終的なexamined_rowsが小さくなることが期待できる
- ソートも必要なくなるので、効率が良くなる
全文検索を実現するインデックス
- MySQLにおける全文検索インデックス
ALTER TABLE [テーブル名] ADD FULLTEXT INDEX [インデックス名(カラム)] WITH PARSER [テキスト分割方式]
WHERE MATCH (column) AGAINST (expr)
という全文検索用の関数で検索できるUsing where; Ft_hints: no_ranking
というオプティマイザのヒントが出る
- 検索・更新時の負荷が上がるので、パフォーマンスに注意する
- もしより高度・高品質な検索を行う場合はElasticsearchなどの使用を検討する
空間インデックス
- 緯度経度などの情報から効率的に検索できるよにするインデックス
- STから始まる関数を利用して絞り込みを行う
- 使用しているデータ構造はRツリー
N+1問題
- 1回のクエリで得たN件の結果に対して、それらに付随する情報を取得するのにN回以上のクエリを実行すること
- データベースに着目されがちだが、外部APIのFetchなど、全てのリソースへのアクセスで発生する可能性がある
N+1の見つけ方
- スロークエリログの実行数が多いクエリをヒントに、アプリケーションの中でループ中にSQLを十個空いている箇所を探す
- New RelicなどのAPMプロファイラの情報を参考にする
- フレームワークによる支援を活用する
キャッシュによる回避
- memcacheやRedisなどに情報をキャッシュすることによって、DB本体に対するクエリ回数を減らす戦略
- N件のデータ
- キャッシュをあたる
- あればそれを使用
- 無ければDBを探す
- DBの結果をキャッシュへ格納
- データベースへの負荷を減らすという効果はあるが、結局キャッシュサーバに対してN回問い合わせをすることになるので、全体の性能向上としては改善の余地がある
- memcacheやRedisなら、一度の通信で複数のキャッシュを取得できる
別クエリによるプリロードを用いた解決
- データベースへのクエリを2回まで減らせる
IN
句を動的に構築し実行する- N件のデータ
IN (N1, N2, N3…)
というクエリを構築- 構築したクエリを実行
- 注意事項
- IN句のサイズが大きすぎるとエラーになる場合がある
- MySQLでは
max_allowed_packet
で設定できる(Default: 64MB)
- MySQLでは
- 狙ったインデックスが使用されない場合がある
- オプティマイザがフルスキャンしたほうが速いと判断する場合がある
eq_range_index_dive_limit
で変更できる(Default: 200)- IN句の件数がこの値以内であれば、操作する行数を正確に見積もろうとする
- インデックスダイブと呼ぶ
- これ以上になるとインデックスの統計データから見積もる
- 高速だが正確性が落ち、フルスキャンを行うという判断がされる可能性がある
- IN句の件数がこの値以内であれば、操作する行数を正確に見積もろうとする
- オプティマイザがフルスキャンしたほうが速いと判断する場合がある
- IN句のサイズが大きすぎるとエラーになる場合がある
- 最適化の最初のステップとして良い
JOINを使用した解決
- JOINしたクエリを使用する
- 1回のクエリで取得できる
- (時と場合によるけど)最も効率が良い
データベースとリソースの効率的な活用
FORCE INDEX
- オプティマイザの判断により希望のインデックスを使用しない場合がある
FORCE INDEX
を使用することによって使用するインデックスのヒントを与えることができる
STRAIGHT_JOIN
- 場合によって、記述とは異なる順序で実行計画が建てられる場合がある
FROM t1 JOIN t2 ON [t1.id](http://t1.id) = t2.id
とした時に、t2
側から処理が開始されるなど
STRAIGHT_JOIN
ヒントを用いることで、クエリに記述した順序で実行されるLEFT JOIN
の場合、STRAIGHT_JOIN
が利用できないので、MySQL8.0.1以降ではJOIN_ORDER
ヒントを利用できる
- 場合によって、記述とは異なる順序で実行計画が建てられる場合がある
- データベースの負荷監視、クエリとその実行計画の監視を行い、継続的に改善していくことが重要
SELECT
句で必要な情報だけ取得する- 不必要な重い情報を取得しないようにする
- プリペアドステートメントに注意する
- データベース側でクエリのテンプレートをキャッシュしておいて、クライアント側は埋め込む変数のみを送ってクエリを実行すること
ADMIN PREPARE
- プリペアドステートメントを準備する
ADMIN CLOSE STMT
- プリペアドステートメントを解放する
- Webアプリケーションでは発行するクエリの種類が多く、キャッシュの効率が良くないため、使用しないことを考慮しても良い
- 使用しない場合、接続文字列にクエリパラメータとして、
interpolateParams=true
を設定する
- 使用しない場合、接続文字列にクエリパラメータとして、
- セキュリティ的な側面だと、SQLインジェクションの可能性があるため、無効化したほうが良い
- コネクションはなるべく永続化して使い回す
- 最大接続数を減らしすぎたことによる接続のオーバーヘッドを防ぐ
- 最大接続数を増やしすぎたことによるメモリ不足に注意する
max_connections
を適度な設定にする
データベース負荷の種類
user
のCPU使用率が高い場合- インデックス不足
- N+1が発生
io-wait
のCPU使用率が高い場合- ストレージへの書き込み・読み込みが多い、ストレージが遅い
- 対策: 読み込み回数・書き込み回数を減らす
- ストレージへの書き込み・読み込みが多い、ストレージが遅い
読み取りの高速化
- データサイズの確認
- 一般的なOSにはディスクキャッシュという機構があり、一度ストレージから読み出したデータはメモリ上にキャッシュしている
- サイズが小さければこのキャッシュに載る
- I/O負荷は発生しない
- 高速化にはデータベースのサイズを知ることが重要
- MySQLであれば
/var/lib/mysql
にある*.ibd
ファイルの合計サイズを確認すると良い
- MySQLであれば
- MySQLには読み込んだデータおよびインデックスをメモリ上に確保する
InnoDB Buffer Pool
という機能がある- データベース専用のキャッシュ
- サイズは
innodb_buffer_pool_size
で設定できる(Default: 128MB)- データベース専用サーバがある場合は物理メモリの80%程度を割り当てると良い
- 活用する場合はOSのによるディスクキャッシュと二重で確保しないように、
open
システムコールのフラグにO_DIRECT
をセットする- MySQLでは
innodb_flush_method=O_DIRECT
をセットする
- MySQLでは
更新の高速化
- 更新がボトルネックとなっている場合、基本的にはスロークエリログに記録されることが多い
- それ以外の場合、コミットしたデータを書き込むところで詰まっている
fsync
を用いて行われているが、これが同期的な実装なので、非同期化すると良い- MySQLでは
innodb_flush_log_at_trx_commit
で制御できる- デフォルトは1で、コミット毎に更新データをREDOログに書き、ログをフラッシュする
- 0にすると更新データを1秒おきにフラッシュする
- 2にするとコミット毎にログに書き、1秒ごとにログをフラッシュする
- 0または2ではクラッシュ時に最大1秒間のデータを失う可能性があるが、パフォーマンスが良い
- MySQLでは
- MySQLではリードレプリカを作成するための更新ログ(バイナリログ)が有効化されているので、必要がない場合無効化するとストレージへの書き込み量を減らすことができる
disable-log-bin = 1
を設定すると無効化できるsync_binlog
を設定するとログの書き込みタイミングが調整できる- デフォルトは1で、1コミットごとに更新ログをフラッシュする
- 0にするとフラッシュ命令を出さず、OSに任せることになる
- 2以上にするとその回数ごとにフラッシュする
Chapter 6 リバースプロキシの利用
nginx
- ノンブロッキングI/O
- スレッドは使用せずワーカープロセスでリクエストを捌いている
- プロセス数は
worker_processes
で設定でき、auto
を設定するとCPUのコア数に自動で設定される
- プロセス数は
- リクエストのヘッダに
Accept-Encoding: gzip
が指定されている場合、そのクライアントはgzipを受け入れることができるクライアントであるその場合、レスポンスのbodyをgzip圧縮して返すことができる
gzip圧縮するとおおよそサイズが1/5になる
nginx
では以下の設定によって利用できるgzip on; gzip_types text/css text/javascript application/javascript application/x-javascript application/json; gzip_min_length 1k;
gzip_types
では、画像は既に圧縮済のため指定できないtext/html
はデフォルトで利用できる
gzip_min_length
はgzip圧縮の対象となる最小のbodyサイズを指定するContent-Length
を見て判断している- 小さすぎるものを圧縮すると逆に大きくなることがある
- 1k程度か、もう少し大きい値が良い
nginx
はビルド時にモジュールを有効にすることができるngx_http_gzip_static_module
- 事前にgzip圧縮したファイルを
nginx
から配信できる- オンデマンドに圧縮する必要がなくなるのでCPU消費を抑えることができる
Zopfli
など圧縮率の高いアルゴリズムを使用し事前に圧縮を行うことで、よりレスポンスサイズを小さくすることができる
- 事前にgzip圧縮したファイルを
ngx_http_gzip_static_module
- 通常はgzipに対応していないクライアントのために、gzipと解凍後のファイル両方をサーバにおいておく必要がある
- このモジュールを有効にすることによって、gzip未対応のクライアントからリクエストが来た時には、オンデマンドにgzipファイルを解凍しレスポンスとして返すため、gzipファイルのみをサーバに設置しておくだけで良くなる
- CPUを消費してしまう問題がある点に注意
- gzipの圧縮率は
gzip_comp_level
で設定できる- 基本デフォルトの6(Zlib: nginxが使用しているgzipライブラリ; のデフォルト)で良い
- gzip圧縮はクライアント-リバースプロキシ間でも、リバースプロキシ-アプリケーションサーバ間でもなるべくやったほうが良い
- ファイルサイズが1/5になるということはネットワーク帯域のスループットは5倍になるということ
nginx
はデフォルトでは都度コネクションを切る設定になっている- HTTP/1.1を使用し、Connectionヘッダに空文字を設定すれば、コネクションが保持される
keepalive n
を利用すると接続処理を減らすことができるkeepalive_requests 10000;
を設定するとコネクションを閉じるまでに受け付ける最大のリクエスト数を指定できる
ssl_session_cache
を設定するとTLSのセッションIDをキーにセッション情報をキャッシュできるlisten 443 ssl http2
と設定すると、nginx
でhttp/2
が利用できるssl_protocols TLSv1.2 TLSv1.3;
と設定することで、使用するTLSのバージョンを指定できるsendfile
とtcp_nopush
は有効にすべきsendfile
- ファイルの読み込みとレスポンス送信に
sendfile
システムコールを使用する - カーネル空間とユーザー空間のメモリのコピーを省略できる
- NFSなどでストレージをネットワークマウントしている場合は問題になる場合があるので注意
- ファイルの読み込みとレスポンス送信に
tcp_nopush
sendfile
を有効にしたときのみ使用できる- いい感じにパケット数を減らしてくれる
open_file_cache
を設定すると、一度open
したファイルをキャッシュしてくれる- 事故が起きやすいため注意する
Chapter 7 キャッシュの活用
- キャッシュに求められる機能
- keyからvalueが取得できるKVSとしての機能
- TTLを定められ、TTLが過ぎたらexpireしてデータを削除する機能
- Webアプリケーションが稼働しているサーバのメモリ上にキャッシュする戦略もある
- 注意点
- シングルプロセス・マルチスレッドのアーキテクチャの場合、並行に読み込み・書き込みができるように適切なロックを取る必要がある
- マルチプロセス・シングルスレッドのアーキテクチャの場合、簡単にはプロセス感でメモリの強雨ができない
- PHPで利用されているAPCuでは共有メモリセグメントを利用して、複数の物理メモリの同じセグメントを共有している
- 実装によってはTTLを自前で実装しないといけない
- デメリット
- デプロイ直後にパフォーマンスが劣化したり、データベースなどへの負荷が高まる可能性がある
- アクセス集中によるサーバ追加などでは、キャッシュが無いサーバが新規で立ち上がるため、よりデータベースへの負荷が高まる危険性がある
- 問題のあるデータをキャッシュしたときに簡単に消せない
- TTLを短めにする
- デプロイ直後にパフォーマンスが劣化したり、データベースなどへの負荷が高まる可能性がある
- ローカルのキャッシュは注意点やデメリットもあるので、あくまでメインはミドルウェアでのキャッシュを利用しつつ、そのミドルウェアへのアクセスも減らしたい時に補助的に使用するのが良い
- 注意点
- キー設計はしっかりしよう
- 同じデータを大量の別のキーで保存したり、別のデータなのに同じキーで保存したりしないようにする
- 自動でシリアライズ・デシリアライズしてくれる機能を利用する場合、シリアライズ手法を途中で変更すると古いデータが読めなくなることに注意する
- キャッシュ利用のメリット
- CPU負荷が大きい処理や時間がかかる処理、ブロッキング時間が長い処理などの実行回数を抑えられるので、パフォーマンスが上がる
- インフラコストが下がる
- レートリミットがある外部リソースの場合、それに達しないようにキャッシュする必要がある
- 大量のリクエストに耐えられる仕組みを比較的容易に構築できる
- CPU負荷が大きい処理や時間がかかる処理、ブロッキング時間が長い処理などの実行回数を抑えられるので、パフォーマンスが上がる
- キャッシュ利用の問題
- 古いデータが表示されることがある
- 不整合が発生することがある
- 更新時にキャッシュの削除・更新を適切に行うことである程度軽減できる
- 不整合が発生することがある
- キャッシュを保存するミドルウェアが新たな障害点となりうる
- ミドルウェアの監視が新たに必要になる
- 再起動などによって全てのキャッシュが失われる可能性がある
- 想定外のデータを表示してしまい、情報流出につながる可能性がある
- 重大なセキュリティインシデントを引き起こしてしまう恐れがある
- 実装が複雑になる
- 原因究明の難易度があがる
- キャッシュ生成と大量リクエストが重なり、高負荷になる可能性がある
- Thundering herd problem
- 古いデータが表示されることがある
- 問題を考慮してもなおメリットが上回ると判断されたときのみキャッシュを利用する
- キャッシュを利用しないで解決できるに越したことはない
- キャッシュ導入時の考慮点
- データの不整合がどこまで許されるか
- 決済情報などの重要データは不整合が致命的になるので、キャッシュは使用すべきでない
- 更新したはずのデータが更新されないと、ユーザーからはバグを疑われる
- データの特性上、本当にキャッシュを使用する必要があるか
- ユーザー情報はあんまりキャッシュが有効でない
- 有効でないキャッシュが増えると、ミドルウェアのリソースが不足する可能性がある
- データの更新頻度はどの程度か
- 頻繁に更新されるデータの場合、キャッシュが有効に活用できない
- 情報の鮮度が重要な場合、更新頻度が低いとUXが悪化する
- データの生成コストを考慮しているか
- 生成コストが低いならキャッシュを利用する必要はない
- 生成コストが高すぎる場合は、永続化に強いストレージに保存すべき
- データの不整合がどこまで許されるか
- TTLの考慮
- データに対して、十分に短いTTLを設定する
- 更新時にキャッシュも同時に更新するようにする
- キャッシュバスター
- URIにキャッシュ操作を意図したクエリ文字列を入れ、クライアントのキャッシュ機構によってキャッシュが利用されることを避ける手法
- 注意点
- サーバのキャッシュキーにクエリ文字列が含まれている
- ファイルが変更されたら必ずクエリ文字列に渡す値を更新する
- ファイルが変更されていないなら同じクエリ文字列を使い続けることでキャッシュの更新を最低限にする
キャッシュの実装
Read-aside caching
- キャッシュにデータがあればそれを返し、無ければデータベース等に取得しに行き、その結果をキャッシュする手法
- メリット
- 実装が単純
- データの整合性を保ちやすい
- アクセスが有ったもののみキャッシュするので効率的
- デメリット
- 初回アクセスやキャッシュがなくなったタイミングで遅くなる
- キャッシュに乗っていないタイミングで大量にリクエストが来ると重い処理が同時に実行される(Thundering herd problem)
- App上やミドルウェアで上手く制御する必要がある
Thunder herd problem
- キャッシュに乗っていないタイミングで大量のリクエストが来ること
- キャッシュが必要なほど遅いOriginに対して更に大量のリクエストが行くため、Originの負荷が相当高まる
- 緩和策
- キャッシュを取得した際にキャッシュの残り時間も取得し、その残り時間が指定した時間を下回っている場合は一定の確率で
expire
しているとみなし、キャッシュの再構築をする
- キャッシュを取得した際にキャッシュの残り時間も取得し、その残り時間が指定した時間を下回っている場合は一定の確率で
State while revalidate
- キャッシュが無ければデフォルト値や古いキャッシュを返し、非同期にキャッシュ更新処理を行う手法
- メリット
- ほとんどのリクエストにおいて高速にレスポンスが返せる
- アクセスが有ったもののみキャッシュするので効率的
- デメリット
- ロジックが複雑になる
- キャッシュの更新を非同期に行うため、ジョブキューなどの非同期処理機構が必要
- キャッシュが無ければデフォルト値を返す実装の場合、キャッシュがないタイミングでリクエストした人には適切なレスポンスを返せない
- 古いキャッシュの場合も、本来なら返すべきでないデータを返している
- Thundering herd problemは解決していない
- キャッシュ更新時の処理が複数実行されないようにする仕組みは必要
- ロジックが複雑になる
バッチ処理などで定期的にキャッシュを更新する
- バッチ処理などで定期的にキャッシュを更新する手法
- AppとOriginは直接通信せず、キャッシュを通してのみデータのやり取りを行う
- メリット
- 実装が比較的容易
- Thundering herd problemが発生しない
- デメリット
- バッチで生成できるデータにしか使用できない
- アクセスが少ないデータについてもキャッシュする必要があるので、ストレージが無駄になり、コストも高くなる
- キャッシュが揮発した時に復旧に時間がかかる可能性がある
- キャッシュが存在しなければデータが取得できない
- バッチが大量に存在する場合に難易度が高くなる
キャッシュの監視
- キャッシュの監視項目
expire
していないのにキャッシュから追い出されたアイテム数(evicted items
)- キャッシュの容量が超過しそうな場合、
expire
する前のデータでも削除されてしまう - もし
evicted items
が増えている場合、キャッシュ容量が足りない可能性がある
- キャッシュの容量が超過しそうな場合、
- キャッシュヒット率(
cache-hit ratio
)- キャッシュヒット数/リクエスト数
- レートが低い場合、キャッシュが有効活用できていない
- Webアプリケーションに変更が入ったタイミングで激変することがある
memcached
のstats
コマンドでの項目名evictions
、get_hits
、get_misses
Redis
のINFO
コマンドの以下の項目evicted_keys
、keyspace_hits
、keyspace_missess
Chapter 8 押さえておきたい高速化手法
外部コマンド実行ではなく、ライブラリを利用する
- 外部コマンド実行のデメリット
- 外部コマンドを実行すると、アプリケーションとは別のプロセスが起動するため、コストが高い
- OSコマンドインジェクション脆弱性を作る可能性がある
- 対策
- 外部コマンド呼び出しを利用しない
- 外部コマンドをプログラム上から呼び出す際に外部から入力されたパラメータを渡さない
- 外部コマンドにわたすパラメータを安全な関数を使用してエスケープする
本番用の設定にし、冗長なログを出力しない
- 開発環境ではログを多く出力する設定になっている場合がある
- 大量のログ出力はパフォーマンスに影響が出るため、無効化する
HTTPクライアントの使用方法
- マイクロサービス間ではHTTPで通信することが多い
同一ホストへのコネクションを使い回す
- TCPコネクションを使い回せない場合、HTTPリクエストを送信するたびにTCPハンドシェイクをしないといけない
- サーバ間の必要な通信回数の増加
- ローカルポートの大量消費
- TLSなどによる暗号化を行っている場合、その設定の交換に伴うハンドシェイクも発生
- CPUを多く必要とする処理であるため、CPUのリソース消費が増える
- 一度確立したコネクションはなるべく使い回すと良い
- 仕様上コネクションを1リクエストの処理中でしか使いまわせない場合、HTTPプロキシサーバにコネクションを保持させることで、コネクションの使いまわしを再現することができる
適切なタイムアウトを設定する
- 外部サービスが常に成功するとは限らないため、レスポンスが返ってくるのに時間が掛かったり失敗する場合がある
- Webアプリケーション上に処理中のリクエストが溜まっていき、高負荷になる可能性がある
- 安定したWebサービスを提供するには適切なタイムアウト設定が必要である
- リクエストの特性を考慮したタイムアウト時間の設定が重要
- GETは短め、POSTは長めなど
同一ホストへのコネクション数の制限を確認する
- ホストによっては同一ホストからのコネクション数に制限がある場合がある
- 負荷対策などのため
- 同一ホストへ大量にリクエストを送る場合、必ず制限を確認する
静的ファイルをリバースプロキシから直接配信する
nginx
ではtry_files
を設定すると、指定されたパスを前から順番にチェックし、ファイルが有ればその内容をレスポンスとして返せる- 無ければ指定のURIへ内部リダイレクトする
- 配信時にアプリケーションでの認証を要求したい場合は
X-Accel-Redirect
ヘッダを利用できる- アプリケーションで認証後、アプリケーションから
X-Accel-Redirect
ヘッダを返すことで、nginx
内の別のpathに内部リダイレクトすることができる - この設定を用いることで、アプリケーションでの処理後、直接
nginx
から静的ファイルを配信することができる
- アプリケーションで認証後、アプリケーションから
- 画像を変更した場合は、URLも同時に変更することを考慮に入れる
- ブラウザ上やCDNでのキャッシュによって変更が表示されるのが遅延する場合がある
HTTPヘッダを活用してクライアント側にキャッシュさせる
- 「Web配信の技術」参照
- サーバで配信しているファルがブラウザなどのクライアントが保持しているコンテンツと同一かどうかを判定する、HTTP条件付きリクエストと呼ばれるリクエストが存在する
- 初回・キャッシュが存在しない場合、リクエストは通常通り送る
- レスポンスとしてヘッダに
Last-Modified
、ETag
のいずれかもしくは両方が返ってくるので、クライアントはその値を保持しておく Last-Modifed
- 最終更新時刻
ETag
- リソース固有のユニークな文字列
- レスポンスとしてヘッダに
- キャッシュが期限切れをSチア後のリクエストでは、リクエストのヘッダとして
If-Modified-Since
、If-None-Match
ヘッダを付与するIf-Modified-Since
ヘッダには保存しておいたLast-Modified
、If-None-Match
ヘッダには保存しておいたETag
ヘッダの内容をそれぞれ付与する- コンテンツに変化がなければ、レスポンスとしてボディが空、ステータスコードが
304 NOT MODIFIED
のレスポンスを返す- ボディを空にできるため、転送量を大幅に減らせる
- コンテンツに変化があれば、レスポンスとして新しいコンテンツデータと更新された
Last-Modified
、ETag
ヘッダを返す
- 初回・キャッシュが存在しない場合、リクエストは通常通り送る
- 更新頻度の低いファイルについては
Cache-Control
ヘッダを活用することで、無駄な通信を減らすことができるCache-Control
- 送り先のシステムにおいて、キャッシュがいつまで有効化を記述できる
- 静的ファイルであれば1年以上の大きな数字を指定しているサービスもある
- 極端に大きな数字であれば1年以上であっても問題ない
Cache-Control
ヘッダでキャッシュを使用している静的ファイルを変更する場合、ファイル名を変更するか、キャッシュバスターを使用する
- 非常に効果が大きい!
- 設定項目が多すぎて全ての使用に対応しているクライアントは少ないので、想定通りに動くか検証することが重要
配信サーバ運用時の注意
- 配信サーバが複数台存在している環境において、
Last-Modified
、ETag
の値にばらつきが存在すると、クライアントが適切にキャッシュを利用できない - 全てのサーバで、ファイルの更新時刻が同じなら同じ
Last-Modified
、更新日時と内容が同じなら同じETag
が生成されることを確認する Last-Modified
のみで十分なケースが多いので、そちらのみ有効にする形でも良い
CDNを使用してHTTPレスポンスをキャッシュする
- CDNの機能(一部)
- 柔軟なキャッシュ設定
- エッジでキャッシュすることで、高速にレスポンスを返せる
- ログの保存
- HTTPリクエスト・レスポンスの書き換え
- DDoS対策
- Web Application Firewall
- 悪意のあるリクエストの遮断など
- 柔軟なキャッシュ設定
Cache-Control
を活用してCDNやProxy上にキャッシュさせる
- メリット
- クライアントの近くのCDNをエッジから直接レスポンスを返せるので、最速である
- アプリケーションサーバへのリクエストを減らせるので、インフラコストやリソース消費を下げられる
- デメリット
- CDNの挙動について詳しくない場合、キャッシュしてはいけないレスポンスをキャッシュするなどの事故を起こしやすい
- アプリケーションの設計を、キャッシュを活用しやすい設計にする必要がある
nginx
におけるレスポンスキャッシュ
proxy_cache
を利用する- デフォルトでは、危なそうなレスポンスは極力キャッシュしない設定になっている
proxy_cache_methods
を設定することでキャッシュするHTTPメソッド指定できる- デフォルトは
GET
とHEAD
- デフォルトは
Set-Cookie
がヘッダが含まれているレスポンスはデフォルトではキャッシュされないproxy_ignore_headers
で挙動を変えられるが、まずアプリケーションの設計の問題を疑う
クラウド事業者のオブジェクトストレージサービスを利用する
- クラウド事業者のオブジェクトストレージサービスを利用する場合、以下の内容を確認する
Content-Type
の決定方法- ブラウザにはMIME Sniffingというファイルの内容から
Content-Type
を類推する機能があるが、セキュリティ上の懸念があるため、X-Content-Type-Options: nosniff
ヘッダを付与して無効化する
- ブラウザにはMIME Sniffingというファイルの内容から
- gzip圧縮できているか
Cache-Control
の設定方法- CDNとの連携
- gzipの設定や
Cache-Control
の挙動
- gzipの設定や
- 使用するCDNやオブジェクトストレージサービスの仕様をよく確認する
Chapter 9 OSの基礎知識とチューニング
- Linuxカーネル
strace
- コマンドを実行したときに使用しているシステムコールを確認できる
- Linuxにおけるネットワーク
- Linux NAPI
- 割り込みとポーリングを組み合わせて割り込まれる回数を減らす手法
- RSS(Receive Side Scaling)
- 複数のコアで受信トラフィックを処理できる技術
- 基本的にTCP/UDPなどのネットワークI/Oは最初に待ち受けたコアのみで処理する
- 復号化などにキャッシュが効くから
- コアの前段にあるパケットキューをコア数分増やして、複数のコアで分散して処理する
- 基本的にTCP/UDPなどのネットワークI/Oは最初に待ち受けたコアのみで処理する
- 複数のコアで受信トラフィックを処理できる技術
- Linux NAPI
- LinuxにおけるストレージI/O
- ブロックサイズ毎にIOを行うので、IOPSやスループットから求められる理論値よりも必ず遅くなる
fio
- 現在利用しているファイルシステムの性能を計測できる
iops
- ストレージのIOPS
bw
- スループット(band width)
lat
- レイテンシ
clat
- ざっくりレイテンシ
lsblk
- ブロックストレージの一覧が見れる
mount
- 現在マウントされているディスクの設定が確認できる
- 高速なストレージはファイル削除の時に、実態を消さずにメタデータだけを消して、定期的なジョブによって完全削除を行う手法を取っている
fstrim
によって、完全削除を行うことができる- 多くのLinux環境では
fstrim
を定期的に実行することによって、削除対象のファイルを完全削除している
- 多くのLinux環境では
- 定期的なジョブで消している都合上、削除のタイミングでストレージへ負荷が集中する
discard
マウントオプションを指定すると、削除時にメタデータだけでなく完全削除も行うTRIM命令
が送信される- 毎回TRIM命令が送信されると負荷が上がるが、毎回完全削除されジョブが発生しないので、負荷が集中することを防げる
- I/Oスケジューラ
/sys/block/vda/queue/scheduler
を見るとどのスケジューラを使用しているか分かる- HDDを主に利用していた時代は、ディスクのシークを最小限に抑える
CFQ(Completely Fair Queueing)スケジューラ
を採用していた - 現在は
mq-deadline(Multi-Queue Deadline)スケジューラ
が採用されているDeadlineスケジューラ
をマルチキューで処理できるように改良されたもの
- 他にもいくつかある
Kyber(Budget Fair Queueing)
BFQ
- eBPFのツールセットであるBCC(BPF Compiler Collection)に、レイテンシを計測したり、
stat(2)
のようなプロセスによって発行されたシステムコールを表示したり、MySQLのスロークエリを表示したりするようなものがある
- LinuxにおけるCPU
top
コマンドについて-1
オプションをつけて起動するか、起動後に1
キーを押すと各CPUコアにおける使用率を確認できる- 1コアに処理が集中しているのか分散しているのかを見分けられる
us
: User- ユーザーランドで動いているプロセスのCPU利用率
sy
: System- カーネルにおけるCPU利用率
- プロセスの
fork
が頻発していたり、コンテキストスイッチが多発している場合、su
の数値が上がる - 1リクエスト1プロセスのWebアプリケーション等では、アクセスが集中した時に高くなる
- Webサーバ(ロードバランサ)でHTTPS終端する際に、CPUの暗号化支援機能(AES-NI等)を利用していると、カーネル側で暗号化・復号化を行うので高くなる
- プロセスの
- カーネルにおけるCPU利用率
ni
: Nice- nice値(優先度)が変更されたプロセスのCPU利用率
- nice値とは、プロセスの優先度
-20
(高)~19
(低) まであるfork
コマンドのデフォルトでは、親プロセスの優先度を引き継ぐnice
コマンドによって変更できるrenice
コマンドによって実行中のプロセスのnice値を変更できるionice
では、I/O処理の優先度を変更できる
- nice値(優先度)が変更されたプロセスのCPU利用率
id
: Idle- 利用されていないCPU
wa
: Wait- I/O待ちのプロセスのCPU利用率
- 非同期処理を利用する、ストレージへの読み書きを減らすことで対策可能
- I/O待ちのプロセスのCPU利用率
hi
: Hardware Interrupt- ハードウェア割り込みを利用しているプロセスの利用率
si
: Soft Interrupt- ソフト割り込みを利用しているプロセスの利用率
st
: Steal- ハイパーバイザによって利用されているCPU利用率
- 仮想化された環境において、管理側のプロセスによって利用されているCPU利用率
- 利用できるはずができなかったCPU時間
- 改善する場合、管理側の設定を変更する必要がある
- ハイパーバイザによって利用されているCPU利用率
効率的なシステム設定
ulimit
- プロセスが利用できるリソースの制限
/proc
以下を見ることでも確認できるSoft Limit
- ユーザ権限で変更できる
Hard Limit
- root権限が必要
Linuxカーネルパラメータ
sysctl
- カーネルパラメータを一時的に変更できるコマンド
- 再起動すると元に戻る
- 要管理者権限
sysctl -a
- 設定されているカーネルパラメータの一覧が見れる
sysctl [パラメータ名]
- パラメータの値の確認
sysctl -w [パラメータ名]=[変更後の値]
- 値の変更
sysctl -p
- 設定の更新
/etc/sysctl.d/
- 永続的なカーネルパラメータの設定が入っている
net.core.somaxconn
- backlogキュー(接続要求キュー)をどの程度受け入れられるのかを設定するカーネルパラメータ
listen(2)
によって受け取ったSYNはbacklogと呼ばれる接続要求キューに追加される- 受け取ったらカーネルによって自動的にSYN+ACKが返される
accept(2)
はそのbacklogから接続要求を取り出し、ACKを受信し(ACKが来るまでブロッキングする)、ソケットのディスクリプタを返す- backlogが溢れた場合、新規接続ができないと判断してパケットを廃棄してしまう
- backlogキュー(接続要求キュー)をどの程度受け入れられるのかを設定するカーネルパラメータ
net.ipv4.ip_local_port_range
- エフェメラルポートの範囲を示す
- ポートをより多く使用したい場合は
net.ipv4.ip_local_port_range=1024 65535
などとする
- ポートをより多く使用したい場合は
- 実はipv6でもこの値を参照している
- エフェメラルポートの範囲を示す
- ポートによる接続よりもUNIX domain socketの方が速い
- 利用可能なポートの探索処理などが減るため
- 利用できるなら利用する
MTU(Maximum Transmission Unit)
- そのネットワークインターフェースから送信できる最大サイズ
- MTUよりも大きなパケットを送信する場合、MTUのサイズまで分割して送信する
- IEEE 802.3によって
1500byte
とされてきた - Jumbo Frame
- MTUをもっと大きくしてスループット向上させる手法
- 一般的に
9000byte
まで拡張されることが多い
udev
- Linuxカーネルにおけるデバイス管理ツール