メモリリークの動的診断
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 |
+-----------+----------------+----------+--------+--------------+----------+----------+--------+--------------+------+-----------+-----------+-------+-------------+------------+