CPU負荷を80%から27%に削減する7つの簡単な最適化

私たちのチームは3年以上にわたり、事業者のネットワークの重要なコンポーネントであるPCRFを開発しています。 Policy and Charging Rules Function(PCRF)-LTEネットワーク(3GPP)で加入者サービスポリシーを管理するためのソリューション。これにより、特定のポリシーをリアルタイムで割り当て、加入者に接続されたサービス、彼の場所、この場所のネットワーク品質を考慮に入れることができます現在の瞬間、時刻、消費トラフィック量など このコンテキストでは、ポリシーとは、加入者が利用できるサービスのサブセットとQoS(サービス品質)パラメーターを意味します。 この分野のさまざまなサプライヤーからのさまざまな製品の価格品質比を分析し、製品を開発することにしました。 そして、2年以上にわたり、PCRFはYota商用ネットワークで正常に動作しています。 ソリューションは完全なソフトウェアであり、通常の仮想サーバーにもインストールできます。 Red Hat Linuxの商用で動作しますが、一般的には他のLinuxシステムの下にインストールできます。



PCRFのすべての機能の中で、最も成功したものが認められました。



実際、この記事の後半では、後者の多くの基準の1つについて説明します。



6か月前にテスト用の画像を投稿したリソースがあり、適切なライセンス 、IOTを実施した機器サプライヤーのリスト 、製品ドキュメントのパッケージ 、開発経験に関するいくつかの英語の記事(Luaについて)ベースのエンジン 、または、さまざまなテストなど )。



パフォーマンスに関しては、多くの評価基準があります。 リソースのテストに関する記事では、使用した負荷テストとツールについて詳しく説明しています。 ここで、CPUの使用などのパラメーターで停止したいと思います。

テストで得られた結果を1秒あたり3000トランザクションおよび次の形式のシナリオと比較します。

  1. CCR-I-加入者セッションのセットアップ、
  2. CCR-U-加入者が消費したトラフィック量に関する情報でセッション情報を更新します。
  3. CCR-T-加入者が消費したトラフィックの量に関する情報を含むセッションの終了。


昨年の第1四半期にリリースしたバージョン3.5.2では、このシナリオのCPU負荷は非常に高く、 80%に達しました。 現在商用ネットワーク上にあるバージョン3.6.0では35%に 、現在安定化段階にあるバージョン3.6.1では最大27%に下げることができました。

画像

このような大きな違いにもかかわらず、奇跡を起こすことはせず、単純に7つの単純な最適化を実行しました。これについては、以下で説明します。 お使いの製品では、上記のいずれかを使用して、CPU使用率を向上させることもできます。



まず第一に、最適化のほとんどはデータベースとアプリケーションロジックの相互作用に関係していると言いたいと思います。 クエリのより思慮深い使用と情報のキャッシングが、おそらく、私たちが行った主なことです。 データベースクエリの時間を分析するには、独自のユーティリティを作成する必要がありました。 実際には、アプリケーションは最初にOracle TimesTenデータベースを使用しましたが、これには開発された監視ツールが組み込まれていません。 また、PostgreSQLを実装した後、1つのツールを使用して2つのデータベースを比較することが正しいと判断したため、ユーティリティを残しました。 さらに、このユーティリティを使用すると、データを絶えず収集するのではなく、必要に応じてデータをオン/オフにすることができます。たとえば、CPU負荷がわずかに増加する商用ネットワーク上で、現時点で問題が発生している要求をすぐに分析する機能があります。

このユーティリティはtt_perf_infoを呼び出し、リクエストのさまざまな段階で費やされた時間を単純に測定します:フェッチ、直接実行、1秒あたりの呼び出し数、合計時間の割合。 時間はマイクロ秒単位で表示されます。 バージョン3.5.2および3.6.1の上位15のクエリは、リンクの表で確認できます。

3.5.2トップ15

3.6.1上位15 (このバージョンの空のセルは値0に対応)



最適化1:コミットの減少


異なるバージョンでtt_perf_infoの出力を注意深く見ると、pcrf.commit呼び出しの回数が12006 から1199回、つまり10回に減少していることがわかります。 私たちの頭に浮かんだ非常に明白な決定は、データベースに実際に変更があったかどうかを確認することであり、コミットに対する肯定的な回答の場合のみでした。 たとえば、UPDATEクエリの場合、PCRFは変更されたレコードの数をチェックします。 0の場合、コミットは行われません。 DELETEでも同様です。



最適化2:MERGEリクエストの削除


Oracle TimesTenに基づいて、MERGEリクエストがテーブル全体にロックを設定することがわかりました。 絶えず競合するプロセステーブルの条件では、明らかな問題が発生しました。 そのため、すべてのMERGE要求をGET-UPDATE-INSERTの組み合わせに置き換えました。 レコードがある場合は更新され、ない場合は新しいレコードが追加されます。 このすべてをトランザクションでラップすることさえしませんでしたが、失敗した場合に関数を再帰的に呼び出しました。 擬似コードでは、次のようになります。

our_db_merge_function() { if (db_get() == OK) { if (db_update() == OK) { return OK; } else { return out_db_merge_function(); } } else { if (db_insert() == OK) { return OK; } else { return out_db_merge_function(); } } }
      
      





実際には、1つのレコードで競合が発生することはほとんどないため、これはほとんど常に再帰呼び出しなしで解決します。



最適化3:サブスクライバーによって消費されるトラフィックの量を計算するための構成キャッシュ


3GPP仕様に従って消費されたトラフィックの量を計算するアルゴリズムは、かなり複雑な構造をしています。 バージョン3.5.2では、設定全体がデータベースに保存され、多対多の関係を持つキーとバッテリーの監視テーブルで構成されていました。 システムは、異なる外部システムからのトラフィックアキュムレーターをPCRFの1つの値に蓄積することもサポートし、この設定はデータベースに保存されました。 その結果、累積ボリュームの次のデータが到着すると、ベースで複雑なサンプルが取得されました。

3.6.1では、ほとんどの構成はxmlファイルで取り出され、このファイルの変更に関するプロセスの通知と、構成情報からのチェックサムの計算が行われました。 また、現在のトラフィック監視サブスクリプション情報は、各ユーザーセッションに接続されたblobに保存されます。 BLOBの読み取りと書き込みは、多対多のリレーションシップを持つテーブルの膨大な選択よりも間違いなく高速で、リソースをあまり消費しません。



最適化4:エクスポートLuaエンジンの数を減らす


Luaエンジンは、PCRFで処理されるタイプCCR-I、CCR-U、およびRARの各リクエストに対して呼び出され、リクエストデータの処理時にサブスクライバのポリシーが変更される可能性が高いため、ポリシー選択アルゴリズムを記述するLuaスクリプトを実行します。 しかし、チェックサムのアイデアはここでその応用を見つけました。 バージョン3.6.1では、実際のポリシー変更が依存する可能性があるすべての情報を別の構造に保存し、そのチェックサムの計算を開始しました。 したがって、エンジンは実際の変更の場合にのみ痙攣し始めました。



最適化5:データベースからのネットワーク構成の削除


ネットワーク構成は、PCRFの最も古いバージョンからデータベースに保存されます。 リリース3.5.2では、論理モジュールがデータベースから接続パラメーターを定期的に読み取り、ネットワークパーツがすべてのネットワーク情報のリポジトリとしてデータベースを使用したため、アプリケーションロジックとネットワークパーツはテーブルのネットワーク設定とかなり重複していました。 バージョン3.6.1では、ネットワーク部分の情報が共有メモリに転送され、データベースに変更が加えられたときにそれを更新する定期的なプロセスがメインロジックに追加されました。 これにより、データベース内の共通テーブルのロックが削減されました。



最適化6:選択的直径解析


PCRFは、Diameterプロトコルを使用して外部システムと通信し、単位時間ごとに多くのコマンドを分析および解析します。 これらのコマンドには通常、多くのフィールド(avp)が含まれていますが、すべてのコンポーネントがすべてのフィールドを必要とするわけではありません。 多くの場合、コマンドの最初の(ヘッダー)部分からのいくつかのフィールドのみが使用されます(宛先/起点ホスト/レルムなど)、またはサブスクライバーまたはセッション、つまりid(多くの場合先頭にもあります)を識別できるフィールドです。 また、1つまたは2つのメインプロセスのみがすべてのメッセージフィールドを使用します。 したがって、バージョン3.6.1では、このコンポーネントについてどのフィールドを読み取る必要があるかを説明するマスクが導入されました。 また、ほとんどすべてのメモリコピー操作が削除されました。 実際、元のメッセージのみがメモリに残っており、すべてのプロセスは必要な部分へのポインタを持つ構造を使用し、データはプロセス内で厳密な必要性によってすでにコピーされています。



最適化7:タイムキャッシュ


PCRFが1秒間に10,000を超えるトランザクションの処理を開始すると、ロギングプロセスが時間とCPUのかなりの部分を占めることが明らかになりました。 生産性を高めるためにログを犠牲にすることもできますが、オペレーターはネットワークおよび特定のコンポーネントで発生していることの全体像を再現できる必要があります。 したがって、私たちは分析のために座って、ログ内の最も頻繁なエントリが日時スタンプであることを発見しました。 もちろん、すべてのログエントリに存在します。 次に、時間の精度を1秒に制限し、現在の時間でラインをキャッシュし、次の1秒だけ書き換えます。



これらの7つの最適化はすべて、経験豊富な高性能開発者にとっては、単純明快であることは間違いありません。 彼らもそのように思えましたが、それを実現し実現したときだけです。 最良の解決策はしばしば表面にありますが、見にくいです。 だから私は要約します:

  1. データが実際に変化していることを確認してください。
  2. テーブル全体のロック数を最小限に抑えてください。
  3. データベースから構成データをキャッシュして削除します。
  4. リスト全体を作成する方が簡単だと思われる場合でも、本当に必要なアクションのみを実行してください。





All Articles