blog

Linuxメモリ管理の最適化:低遅延/高スループット・データベースのためのGraphDB

この記事は、LinkedInのエンジニアApurva MehtaがBlogで共有した「Linux Memory Management Optimisation for Low Latency/High...

Jun 8, 2014 · 13 min. read
シェア

簡単

GraphDBは、LinkedInのリアルタイム分散ソーシャルグラフサービスのストレージレイヤーとして機能します。このサービスは、単純なクエリと複雑なクエリの両方を処理できるように設計されています。幅広いノードタイプとバウンダリータイプをサポートし、実行中のすべてのクエリを直接処理することができます。ご興味のある方はソーシャルグラフを使用するアプリケーションの入門的なブログ記事をご覧ください。

LinkedInのすべてのページビューは、GraphDBに複数のクエリ要求を生成します。これは、GraphDBが毎秒何千ものクエリーリクエストを処理していることを意味し、これらのクエリーの99%はマイクロ秒レベルのレイテンシー内で応答されます。このことを考慮すると、GraphDBの応答レイテンシがわずか5ミリ秒に増加したとしても、LinkedInのグローバルアクセスの有効性は深刻な影響を受けることになります。

2013年の多くの期間、GraphDBはピーク時の断続的な応答遅延に悩まされていることが判明しました。これらのピーク時を詳細に調査し、LinuxカーネルがNUMAシステム上で仮想メモリをどのように管理するかを理解する努力をしました。要約すると、NUMAに対するLinuxの最適化の一部には深刻な悪影響があり、結果としてレイテンシに直接的な悪影響を及ぼす可能性があります。この研究の結果は、高いレイテンシを必要とする Linux 環境で稼動しているオンラインデータベースシステムが性能向上を達成するのに十分なものであると考えられます。最適化の結果、問題が発生する可能性は以前の4分の1に減少しました。

記事の***パートでは、データ管理のためのGraphDBのプロセスの概要、パフォーマンス問題の詳細、Linuxの仮想メモリ管理サブシステムの仕組みに関する背景情報を共有します。記事の後半では、実験を通して問題の根本的な原因を見つけることを目的とし、解決策、ガイダンス、結論の要約を詳しく探ります。*** この例を通して学んだ教訓をまとめます。

この記事は、オペレーティング・システムのメカニズムについてある程度の知識をお持ちの方に適しています。

背景情報

1) GraphDBのデータ管理方法

GraphDBは基本的にインメモリデータベースです。読み込み側では、すべてのデータファイルは仮想メモリページにマッピングされ、常にアクティブセット下のメモリに残ります。読み込みアクティビティはデータセット全体を対象として高度にランダム化されており、リクエストの99%はレイテンシをマイクロ秒レベルに抑える必要があります。典型的なGraphDBホストは、48GBの物理メモリを持つことができ、20GBのコモンメモリがあります:オフヒープ仮想メモリページマップされたデータを処理するための15GBとJVMヒープ用の5GBです。

そして書き込み側では、ログ構造のストレージシステムを備えています。データ全体は、10MB単位で純粋に追記可能なセクションに分割されています。現在、各GraphDBホストには約1,500のアクティブなセクションがあり、そのうちいつでも書き込み操作を受け付けることができるのは25のみで、残りの1,475は読み取り専用状態です。

データはログ構造のストレージに保存されるため、定期的に圧縮する必要があります。さらに、積極的な圧縮スケジュールを採用しているため、各ホストで毎日約900個のデータ断片が破棄されます。言い換えれば、1ホストあたり1日あたり約9ギガバイト(GB)のデータファイルが完全に消滅しますが、それでもLinkedInのグローバルなデータ増量のごく一部にすぎません。まとめると、48GBの各ホストで5日間運用すると、ページキャッシュはゴミでいっぱいになります。

2) 問題の症状

パフォーマンスの問題は、主にピーク時のGraphDBのレスポンス遅延という形で発生しました。これらのピークは、sarで示されるように、多くのダイレクトページスキャンと非効率的なインメモリ実行を伴うことがよくあります。場合によっては、sar -Bの "pqsand per second "カラムの出力効率が1秒あたり100万から500万ページスキャンまで低下し、仮想メモリ効率が0%まで低下します。

これは、/proc/meminfoログから確認できる無効なキャッシュページの数が多いためです。さらに、pqscand/sのすべてのピーク時間がGraphDBの待ち時間の問題を引き起こすわけではありません。

最も混乱する質問は2つ:

1、システムに明らかなメモリ不足がない場合、なぜカーネルはページをスキャンするのですか?

2.カーネルがページのスキャンを開始したにもかかわらず、応答レイテンシが劇的に増加するのはなぜですか?書き込みスレッドだけが新しいメモリ割り当てを行う必要があり、書き込みスレッドプールと読み取りスレッドプールは互いに独立しています。したがって、実際には双方が互いに影響し合っていることが判明するのはなぜでしょうか?

これらの疑問に対する答えが、NUMAシステム向けのLinux最適化スキームの適応につながりました。最終的にLinuxの「ゾーンリサイクル」機能に焦点が当てられたことを強調しておきます。NUMA、Linux、ゾーンリサイクルについてあまりご存じない方もご心配なく。この情報があれば、残りの議論も理解できるはずです。

3) Linux、NUMA、ゾーンリサイクルのすべて

この問題の根本原因を理解するには、まずLinuxシステムがNUMAアーキテクチャをどのように扱うかを理解する必要があります。その背景を素早く把握するのに役立つと思われる、優れたプレゼンテーション・リソースを選びました:

また、Linuxがページキャッシュからリサイクルされたページを使用するメカニズムを正確に理解することも重要です。

つまり、LinuxはNUMAゾーンごとに、アクティブリスト、非アクティブリスト、フリーリストの3つのページリストを保持します。新しいページが割り当てられると、空きリストからアクティブリストに転送されます。LRUアルゴリズムは、アクティブリストから非アクティブリストへ、そして非アクティブリストからフリーリストへのページ転送を担当します。以下に、Linuxのページキャッシュ管理について学ぶための****ソースを推薦します:

まず、上記の情報をよく読み、本番ホストでゾーンリサイクルモードをオフにしてみました。オフにした直後から、パフォーマンスは大幅に改善されました。このことから、ゾーンリサイクルがどのように機能し、なぜパフォーマンスに影響するのかについて、本稿で詳しく説明することにしました。

この記事の残りはLinuxのゾーンリサイクルについて掘り下げていますので、ゾーンリサイクルについてよく知らない方は、前回の記事で推奨したJeff Frost氏の議論をまずお読みください。

リナックスのゾーン・リサイクル活動の再現と理解

1) 実験環境の構築

ゾーン・リサイクルがどのようにトリガーされるのか、またゾーン・リサイクルが パフォーマンスにどのような影響を与えるのかを理解するため、GraphDBの読み込みと 書き込みをシミュレートするプログラムを作成しました。このプログラムを24時間実行し、最初の17時間はゾーン回収モードをオンにしました。最初の17時間はゾーン回復モードをオフにし、7時間はゾーン回復モードをオフにしました。プログラムは24時間の全期間、中断することなく実行され、環境における唯一の 変化は、/proc/sys/vm/zone_reclaim_modeに "0 "を書き込むことにより、17時間目に ゾーン再生が無効になったことです。

ここでは、このプログラムが何をするのかを説明します:

1.2500個の10MBのデータファイルをページキャッシュにマッピングし、すべて読み込んでからマッピングをキャンセルするので、Linuxのページキャッシュはゴミデータでいっぱいになります。その結果、数日間の通常運用の後、システムはGraphDBホストのように動作します。

2.読み取りスレッドのセットは、別の2500個の10MBファイルをページキャッシュにマッピングし、その内容の一部をランダムに読み取ります。これらの2500ファイルはアクティブセットを構成し、GraphDBの日常的な使用時の読み取り状態をシミュレートするために使用されます。

3.書き込みプロセスのセットは、10MBのファイルを連続的に作成します。ファイルが作成されると、書き込みスレッドはアクティブなセットからランダムにファイルを選択し、そのマッピングをキャンセルして、作成されたばかりの新しいファイルと置き換えます。このプロセスは、GraphDBの日常的な書き込みアクティビティをシミュレートすることを目的としています。

4, ***, 読み出し完了までのスレッド消費時間が100ミリ秒以上の場合、自動的にusr, sys, アクセス処理の経過時間が出力されます。

プログラムの実行に使用されたメインフレームコンピューターの物理メモリーは48GB。ワークグループは、システム上で他のタスクが実行されていない状態で、このうち約25GBを占有します。こうすることで、ホストがメモリ不足に陥らないことが保証されます。

Githubでシミュレーションのプロセスを見るには、ここをクリックください。

2) ゾーンリサイクルの仕組みの理解

プロセスがページを要求すると、システム・カーネルはまず、*** NUMAゾーンに十分な空きメモリがあり、再生可能なページが1%以上あるかどうかをチェックします。この割合は調整可能で、vm.min_unmapped_ratio sysctlによって決定されます。再利用可能なページはファイル・バッキングされたページですが、現在はどのプロセスにもマップされていません。proc/meminfoでは、いわゆる「リサイクル可能なページ」が「active-unmapped-mapped」であることは明らかです。

では、カーネルはどのようにして空きメモリの十分な量を決定するのでしょうか?メモリはゾーン "レベルマーカー "を使用し、これは/proc/sys/vm/min_free_kbytesの値によって決定されます。また、/proc/sys/vm/lowmem_reserve_ratio の値もチェックします。特定のホストについて計算された値は、以下に示すように、「low/medium/high」ラベルを付けて /proc/zoneinfo に保存されます:

Node 1, zone Normal 
pages free 17353 
min 11284 
low 14105 
high 16926 
scanned 0 
spanned 6291456 
present 6205440 

メモリは、ゾーン内の空きページ数がレベルマークより少ない場合にページ再生を実行します。空きページ数が "low "レベルマークを上回ると、再利用は中止されます。さらに、この計算は個々のゾーンごとに行われます。他のゾーンにまだ十分な空きメモリがある場合でも、現在のゾーンがトリガーされるたびに再生メカニズムが実行されます。

以下の表とグラフは、実験中の活動のパフォーマンスを示しています。注目すべき点は以下の通り:

  • 黒い線はゾーン内のページスキャンを表し、右側にY軸を持つグラフを生成します。
  • 赤線はゾーン内の空きページ数。
  • 緑の線は「低」レベルマーカー。

実験を通じて、本番ホストと同様の動作が観察されました。すべての場合において、ページスキャンは空きページと一致しており、値は逆になっていました。言い換えれば、Linuxは空きページ数が「低」レベルマークを下回るとゾーン再生メカニズムを起動します。

3)システム領域回復メカニズムの特徴

ゾーン・リサイクル・モードのトリガー原理を理解した上で、以下では別の側面、つまりゾーン・リサイクル・モードがパフォーマンス性能にどのような影響を与えるかに注目します。そのために、実験中、以下のソースからの情報を1秒に1回収集しました:

  1. /proc/zoneinfo
  2. /proc/vmstat
  3. /proc/meminfo
  4. numactl -H

これらのファイルのデータをグラフ化し、アクティビティーのパターンを要約した結果、非常に興味深い特徴が発見されました。

予備的な観測では、ゾーン・リカバリ・メカニズムが有効化されており、その時点で Linuxはほとんど直接的にリカバリを実行していました。ゾーン・リカバリ・モードが無効になると、直接のリカバリ動作は直ちに停止しますが、kswapdによって実行されるリカバリの数は増加し始めます。これにより、sarで1秒あたりのpqscandがこれほど高くなる理由が説明できます:

第2に、ゾーン再生利用モードをオフにした場合、読み取りと書き込みの操作に変化はないにもかかわらず、Linuxのアクティブリストと非アクティブリスト内のページ数が大きく変化しました。ゾーン再生メカニズムがオンの場合、Linuxはアクティブリストに合計約20GBのページ情報を保持します。一方、ゾーンリサイクル機構をオフにした場合、Linuxはアクティブリスト内に保持する情報量を、作業セット全体のサイズである約25GBまで増加させます:

*** ゾーン回復モードをオフにした後、ページ内アクティビティに有意な差が見られました。注目すべき点は、ページキャッシュからディスクレジデンシーへの転送率は一定であるのに対し、ディスクレジデンシーからインページへの転送率は、ゾーンリカバリーモードがオフになった後に大幅に減少していることです。主要故障率の変化は、ページ内転送率とまったく同じです。以下のグラフはこの結論を裏付けるものです:

*** ゾーン再生モードをオフにした後、プログラムのメモリアクセス占有率が大幅に減少することが判明。以下のグラフは、システム CPU レベルとユーザー CPU レベルでのメモリ・アクセス待ち時間と、応答時間の具体的な消費量を示しています。プログラムの実行時間のほとんどは、I/O 待機とシステム CPU での時折のブロッキングによって消費されていることがわかります。

4) ゾーン回復モードが読み取り性能に与える影響

上記の統計に基づくと、ゾーン・リサイクルをトリガーとする直接的なリサイクル・パスは、アクティブ・リストからページを削除し、非アクティブ・リストへ移動させることに積極的すぎる可能性があるようです。特に、ゾーン再生利用が有効になっている場合、アクティブなページが単純に巻き取られて非アクティブリストに詰め込まれた後、空きリストに移動されるようです。その結果、読み取り活動は非常に高い確率で大きな失敗を経験し、性能は低下します。

shrink_inactive_list 関数は、直接的なリカバリパスの一部として、ゾーンにグローバルスピンロックを適用し、リカバリプロセス中に他のスレッドがゾーンを変更できないようにします。このため、複数のスレッド間の競合が原因である可能性が高いのですが、ピーク時に読み取りスレッドでシステムCPUがロックアップされることが判明しました。

NUMAメモリバランシングメカニズムはまた、ダイレクトページスキャンをトリガーします。

ゾーン・リクレイムモードがどのようにダイレクト・ページ・スキャンをトリガーするのかがわかり、ページ・データをアクティブ・リストから押し出すこのタイプのスキャンが、読み取りパフォーマンス低下の原因であることが確認されました。ゾーン・リクレイムに加え、Transparent HugePagesと呼ばれるRed Hat Linuxの機能も、NUMAゾーンでメモリの「バランシング」が実行される際にダイレクト・ページ・スキャンを引き起こすことが判明しています。

THP機能により、システムは匿名メモリ用に2MBの「ラージページ」を透過的に割り当てます。これにより、TLB のヒット率が向上し、システム内のページリストのサイズが小さくなります。Red Hat によると、THP は特定のワークロードで最大 10 パーセントのパフォーマンス向上を実現するとのことです。

また、この機能は透過的に動作するため、大きなページを複数の通常のページに分割したり、複数の通常のページを巨大なページに集約したりするコードの一部も使用します。

ゾーンにメモリ圧力がない場合でも、thp_splitによって高い直接ページスキャン比率が得られることが確認されています。また、Linuxシステムが5GBのJavaヒープを巨大なページに分割し、異なるNUMAゾーン間で移動させることも判明しています。以下のグラフを参照してください。2つのNUMAゾーンから合計約5GBのデータが巨大ページに分割され、その一部がノード1からノード0に移動しています。

私自身の実験環境ではこの状態を再現することはできず、ダイレクト・ページ・スキャンの一般的な原因ではないようです。しかし、Transparent HugePages機能がNUMAシステムでうまく機能しないことを証明するデータは十分にあるため、私自身のRHELアプライアンスで以下のコマンドを実行し、この機能を無効にすることにしました。

echo never > /sys/kernel/mm/transparent_hugepage/enabled 

教訓

1) LinuxのNUMA最適化は、典型的なデータベース負荷には意味がない

データベースの主な性能向上は、大量のデータをメモリにキャッシュすることに由来します。ディスクの読み取りと書き込みを回避するためにメモリ・キャッシングを使用することによって節約される時間は、マルチソケット・システムの特定のスロットにメモリを差し込むことによって得られるレイテンシの改善をはるかに上回ることを強調することが重要です。

LinuxのNUMA最適化メカニズムは、パフォーマンスを向上させるためにいくつかの方法で無効にすることができます:

  • ゾーン再利用モードをオフにします。/etc/sysctl.confにvm.zone_reclaim_mode = 0を追加し、sysctl -pを実行して新しい設定をロードします。
  • アプリケーションの実行時に、numactl --interleave=allコマンドを追加してください。

この2つの設定は、すべてのプロダクション・システムのデフォルトになっています。

2) Linuxのセットアップを軽視しないでください:ページキャッシュのスパムを手作業で管理してください!

GraphDBのロギング構造化ストレージシステムは、データフラグメントを再利用できないため、Linuxページキャッシュ内の多くのゴミコンテンツに時間をかけて耐えてきました。Linuxはこのゴミコンテンツを適切にクリーンアップするのが非常に苦手であることが判明しました。Linuxは通常、何も考えずにすべてをゴミ箱に捨てますが、この過度に積極的な処理により、読み込みパフォーマンスが非常に高い確率で大きな失敗に見舞われました。直接リサイクルとkswapdの両方がこの問題の原因ですが、前者の悪影響の方がより顕著です。

はスニペットのプールを独自のストレージ・システムに追加し、これらのスニペットを再利用できるようにしました。こうすることで、作成する必要があるファイルの数を減らすと同時に、Linuxのページキャッシュに対する処理圧力を減らすことができます。フラグメント・プーリング・メカニズムの初期テストでは、有望で優れた結果が得られています。

で書かれた言葉

ゾーン・リカバリーメカニズムに疑念が生じて以来、生産システムでこのモデルをオフにする作業が直ちに開始されました。この調整は大きな成果をもたらしました。過去4ヶ月間、本番ホストの遅延は中央値でした。拍手や花束を贈ることなく、ゾーン・リサイクルをオフにするという小さな決断が、LinkedInの顕著なパフォーマンス向上という、技術者にとっては****の喜びをもたらしました!

Read next

Android OpenGLes 3.0学習:頂点描画メソッド VBOs

現在、android 4.3以上のみがopengles 3.0をサポートしていますが、opengles 3.0をサポートできるandroid 4.3を搭載したハードウェアはほとんどありません。しかし幸いなことに、opengles 3.0は後方互換性があり、アプリケーションがハードウェアがopengles 3.0をサポートしていないと判断すると、自動的にopengles 2.0 APIを呼び出します。

Jun 7, 2014 · 6 min read