メモリリークの動的診断
GV$OB_MEMORYビューは、OBServerノード全体でObMallocによって割り当てられたメモリの状況を表示します。各メモリブロックにはlabelまたはmod_idと呼ばれるラベルが付けられており、modの統計情報を確認することで、システムメモリの使用状況を分析し、メモリリークが疑われるモジュールを初期段階で特定できます。
メモリリーク問題の調査を容易にするため、OceanBaseデータベースではメモリリークの動的診断メカニズムが実装されています。その仕組みは、モジュールがメモリを割り当てるたびに、申請したメモリのアドレスと対応するコールスタックを記録し、割り当てられたメモリが解放されるとそのレコードをクリアすることです。このように、正常にメモリを申請・解放するコールスタックはすぐにクリアされますが、メモリリークが発生しているコールスタックの記録は残り続けます。最終的に、コールスタックごとにグループ化し、各異なるコールスタックに現在存在する申請レコードを累積して、__all_virtual_mem_leak_checker_infoに表示します。言い換えれば、累積回数が多いコールスタックはメモリリークが発生している可能性が高いです(もちろん、さまざまなキャッシュによるものである場合もあります。これは具体的な問題に応じて分析する必要があります)。
memory_leakを開く
obclient> alter system set leak_mod_to_check='OB_COMMON_ARRAY';
このように、トレースモジュールをOB_COMMON_ARRAYと指定します。設定が完了したら、__all_virtual_mem_leak_checker_info テーブルの情報を監視し始め、問題が発生する可能性のあるコールスタックを確認できます。
クエリ
obclient> select * from __all_virtual_mem_leak_checker_info order by alloc_count desc;
一般的に、情報漏洩が発生した呼び出しスタックのカウントは非常に大きく(しかもますます大きく)なります。このような呼び出しスタックをaddr2lineを用いて出力します。
obclient> addr2line -pCfe bin/observer 0xe77df5 0xedf5a4 0xee14b0 0xee0923 1 0xeea558 0x4a05c3 0x1485cd9 0x1485223 0x1483c4b 0x1526f2a 0x1401359 0x1403075 0x140325a 0x1406416 0x14a51bb 0x14a5130 0x140x48f3aa7db8 0x14a4cc8 0x14a50f4 0x14a5cb1 0x130166e 0xd08bc9 0xd069d2 0xd01e97 0xeff033 0xefe6
$oceanbase_root/src/lib/utility/utility.cpp:58
$oceanbase_root/src/lib/../../src/lib/allocator/ob_mem_leak_checker.h:125
$oceanbase_root/src/lib/allocator/ob_tc_malloc.cpp:399
$oceanbase_root/src/lib/allocator/ob_tc_malloc.cpp:218
$oceanbase_root/src/observer/../../src/lib/allocator/ob_malloc.h:38
$oceanbase_root/src/lib/allocator/ob_malloc.cpp:121
$oceanbase_root/src/lib/../../src/lib/allocator/ob_malloc.h:116
$oceanbase_root/src/sql/../../src/lib/container/ob_array.h:295
$oceanbase_root/src/sql/../../src/lib/container/ob_array.h:291
$oceanbase_root/src/sql/../../src/lib/container/ob_array.h:468
$oceanbase_root/src/sql/optimizer/ob_log_table_scan.cpp:85
$oceanbase_root/src/sql/optimizer/ob_log_plan.cpp:1183
$oceanbase_root/src/sql/optimizer/ob_log_plan.cpp:1484
$oceanbase_root/src/sql/optimizer/ob_log_plan.cpp:1557
$oceanbase_root/src/sql/optimizer/ob_log_plan.cpp:2092
$oceanbase_root/src/sql/optimizer/ob_select_log_plan.cpp:467
$oceanbase_root/src/sql/optimizer/ob_select_log_plan.cpp:455
$oceanbase_root/src/sql/optimizer/ob_select_log_plan.cpp:890
$oceanbase_root/src/sql/optimizer/ob_select_log_plan.cpp:378
$oceanbase_root/src/sql/optimizer/ob_select_log_plan.cpp:450
$oceanbase_root/src/sql/optimizer/ob_select_log_plan.cpp:568
$oceanbase_root/src/sql/optimizer/ob_optimizer.cpp:23
$oceanbase_root/src/sql/ob_sql.cpp:1160
$oceanbase_root/src/sql/ob_sql.cpp:887
$oceanbase_root/src/sql/ob_sql.cpp:152
$oceanbase_root/src/observer/mysql/obmp_query.cpp:263
memory_leakを無効にする
チェッカーを有効にすると、実行パフォーマンスが著しく低下します。問題の特定後は、速やかにチェッカーを無効にしてください。
obclient> alter system set leak_mod_to_check='';
memory_contextメモリ動的リークチェック
長いライフサイクルのメモリ管理においては、memory_leakが優れた診断機能を提供し、問題の発生箇所を呼び出しスタックで容易に特定できます。しかし、短いライフサイクルのモデルでは、メモリはすぐに解放されるため、memory_leakはしばしば無力です。この問題を解決するために、新しい機能が必要とされています。
memory_contextは、OceanBaseデータベースがライフサイクルに基づいてメモリを管理するためのインターフェースです。memory_context自体がメモリのライフサイクルを表しているため、memory_contextの機能を拡張して、短いライフサイクルのメモリリーク診断を実現できます。注意点として、memory_contextはリークが発生した場合でも、メモリが完全に解放されることを保証します。したがって、正確に言えば、「短いライフサイクルのメモリの動的リーク」をサポートしているということです。
static_idの特定
ログに"HAS UNFREE"というメッセージが出力されたら、そのログ内のstatic_idを見つけます。
[2020-10-22 15:30:57.923806] ERROR has_unfree_callback (object_set.cpp:27) [67779][462][Y40DC64589025-0005B23D7165EB9F] [lt=24] [dc=0] HAS UNFREE PTR!!!label: mytest1,static_id: 12,static_info: {filename_:"obmp_query.cpp", line_:475, function_:"process_single_stmt"}, dynamic_info: {tid_:67779, cid_:462, create_time_:1603351857840041}
memory_leakの起動
obclient> alter system set leak_mod_to_check='mytest1@12';
これは、static_idが12のmemory_context配下のmytest1のメモリ申請を追跡することを示しています。もちろんワイルドカードも使用できますが、本番環境では推奨されません。パフォーマンスとメモリ使用量に影響があるため、可能な限り範囲を絞ることが合理的です。
obclient> alter system set leak_mod_to_check='*@12';
このように書くと、static_idが12のmemory_context配下のすべてのメモリ申請を追跡することを示します。
再発生を待つ
次回HAS UNFREEが発生すると、呼び出しスタックが出力されます。
[2020-10-22 15:41:02.530881] INFO object_set.cpp:523 [67784][472][Y40DC64589025-0005B23D7135EBC2] [lt=15] [dc=0] CONTEXT MEMORY LEAK. ptr: 0x7f8f8a4145b0, size: 200, label: mytest1, lbt: 0xb979d8b 0xb72d239 0xb71ad7a 0x3a88b97 0x7fd735c 0x7fcee89 0x7fcd41d 0xbac5fad 0x8044c7a 0x80443b5 0x804001a 0x803f2ff 0x32e2cd7 0x32e2b7c 0x2d49d6d 0xb76f163 0xb76cd74 0xb76bdae
同様に、addr2lineを使用してさらに分析を続けることができます。
memory_leakの停止
checkerを起動すると実行パフォーマンスが著しく低下するため、問題の特定後は速やかにcheckerを停止してください。
obclient> alter system set leak_mod_to_check='';
メモリメタデータのダンプ
メモリ内のすべてのメタデータをディスクに書き出すことができます。
操作手順
OceanBaseデータベースのデプロイメントディレクトリを開きます。
etc/dump.configファイルを新規作成し、対象のコマンドを記述します(具体的なコマンドは後述)。kill -62 $pidを実行し、log/memory_metaファイルを確認します。
コマンド一覧
すべてのメモリ情報を出力する
dump chunk all指定したテナント+ctxのメモリ情報を出力する
dump chunk tenant_id=$tenant_id,$ctx_id=ctx_id(数字のみ使用してください)
glibcインターフェースが申請したメモリの確認
mallocなどのglibcメモリ申請は、OceanBaseデータベースのメモリ管理インターフェースに透過的に渡され、mod_idはglibc_mallocとなります。以下の方法で確認できます:
obclient> select *from GV$OB_MEMORY where mod_name = 'glibc_malloc' and hold > 0;
+-----------+----------------+----------+--------+--------------+----------+----------+--------+--------------+------+-----------+-----------+-------+-------------+------------+
| tenant_id | svr_ip | svr_port | ctx_id | label | ctx_name | mod_type | mod_id | mod_name | zone | hold | used | count | alloc_count | free_count |
+-----------+----------------+----------+--------+--------------+----------+----------+--------+--------------+------+-----------+-----------+-------+-------------+------------+
| 500 | xx.xx.xx.xx | 46824 | 26 | glibc_malloc | GLIBC | user | 0 | glibc_malloc | z1 | 249800208 | 219156754 | 41247 | 0 | 0 |
+-----------+----------------+----------+--------+--------------+----------+----------+--------+--------------+------+-----------+-----------+-------+-------------+------------+