SQLおよびトランザクションリクエストを処理するスレッドはワーカースレッドとも呼ばれ、サブテナントごとに割り当てられます。つまり、各テナントは独自のSQL/トランザクションワーカーを持ちます。
マルチテナントスレッドプール
スレッドプールの概要
マルチテナントは大規模なスレッドプールを共有し、各テナントはスレッドプールからワーカースレッドを申請します。スレッドプールはマルチテナントと共に初期化および破棄されます。マルチテナントが初期化される際には、あらかじめ一定数のスレッドが予約され、その後のテナントの実行中にも動的にスレッドプールを拡張できます。
初期スレッドプールサイズは、OBServerノードのCPU数、システムテナント用予約スレッド数、仮想テナント用予約スレッド数に関連しており、その一部は構成パラメータで設定できます。その後のスレッドプール拡張時のスレッド数の上限は、OBServerノードのスレッド数上限とマルチテナントスレッド数上限によって共同で制限され、これも構成パラメータで設定可能です。
スレッドプール関連の構成パラメータ
_ob_max_thread_num
OBServerノードのスレッド数上限を決定します。
デフォルト値は9999で、取り得る範囲は[0, 10000)です。動的に反映されません。
server_cpu_quota_min
システムテナントの最小仮想CPU数を決定します。マシンの物理CPUとは直接関係なく、マルチテナントスレッドプールの初期サイズにのみ影響します。
デフォルト値は0で、取り得る範囲は[0, 16]です。動的に反映されます。
server_cpu_quota_max
システムテナントの最大仮想CPU数を決定します。マシンの物理CPUとは直接関係なく、マルチテナントスレッドプールの上限サイズにのみ影響します。
デフォルト値は0で、取り得る範囲は[0, 16]です。動的に反映されます。
election_cpu_quota
electionテナントの仮想CPU数を決定します。マシンの物理CPUとは直接関係なく、マルチテナントスレッドプールの初期サイズと上限サイズにのみ影響します。
デフォルト値は3で、取り得る範囲は[0, 16]です。動的に反映されません。
location_cache_cpu_quota
location cacheテナントの仮想CPU数を決定します。マシンの物理CPUとは直接関係なく、マルチテナントスレッドプールの初期サイズと上限サイズにのみ影響します。
デフォルト値は5で、取り得る範囲は[0, 16]です。動的に反映されません。
テナントスレッド
テナントスレッド構成
単一テナントのスレッドには、ネストされたリクエストを処理する7つの専用スレッドと、一般的なリクエストを処理する複数の汎用スレッドが含まれます。スレッドはアクティブ状態とスレッド状態の2種類にあります。ネストされたリクエストを処理する専用スレッドは外部からほとんど認識できないため、以下の説明では汎用スレッドのみを対象とします。
アクティブスレッド数の概念
アクティブスレッドとは、正常にリクエストを処理できるスレッドを指し、スレッド状態のスレッドとは区別されます。アクティブスレッド数は、単一テナントのCPU使用量を制限するために使用されます。テナントランタイムはアクティブスレッド数を一定に保ち、テナントのアクティブスレッド数はパラメータとUnit仕様によって共同で決定されます。アクティブスレッド数 = unit_min_cpu × cpu_quota_concurrency。
大規模クエリのスレッドスレッドロジック
ユーザーがOBServerノードに送信するSQLは、大まかに2つのカテゴリに分けられます。1つはアクセスおよび操作するデータ量が少ないため実行が非常に速いSQLです。もう1つは、大量のデータにアクセスしたり、大量のデータを書き込んだりする必要があるため、実行に時間がかかるSQLです。後者の時間のかかるSQLを大規模クエリと呼びます。
大規模クエリの特別処理は、ユーザー数への影響と遅延のQoS(Quality of Service)要件に基づく直感的なユーザー効用仮説に基づいています。すなわち、1000個の小規模クエリが遅延する影響は、1個の大規模クエリが遅延する影響よりもはるかに大きいと合理的に推測されます。言い換えれば、1秒のCPU時間を使って1個の大規模クエリを処理するよりも、同じCPU時間を使って1000個の小規模クエリを処理する方が効率的です。
リクエストが大規模クエリとして判定される条件は、処理時間が大規模クエリのしきい値を超えることです。このしきい値はパラメータで調整可能です。
テナントスレッドの中で、大規模クエリと判定されたリクエストを保持しているスレッドの一部は直接実行を続行する権利を得られますが、残りのスレッドはスレッド状態で待機する必要があります。実行を続行する権利を得られるスレッドの割合はパラメータで決定されます。
最大スレッド数の概念
テナントのアクティブスレッド数を一定に保ちながら、大規模クエリスレッドのスレッド状態が発生することを考慮するために、テナントはマルチテナントスレッドプールから動的にスレッドを申請する必要があります。最大スレッド数は、単一テナントのメモリ消費を制限するために使用されます。各テナントが保持できる最大スレッド数は、パラメータとUnit仕様によって共同で決定されます。最大スレッド数 = unit_max_cpu × workers_per_cpu_quota。
関連パラメータ
cpu_quota_concurrency
テナントのアクティブスレッド数とテナントUnit仕様の倍数関係を決定します。
デフォルト値は4、値の範囲は[1, 20]、動的に反映されます。
workers_per_cpu_quota
テナントの最大スレッド数とテナントUnit仕様の倍数関係を決定します。
デフォルト値は10、値の範囲は[2, 20]、動的に反映されます。
large_query_worker_percentage
テナントスレッドの中で大規模クエリの実行を続行する権利を持つスレッドの割合を決定します。
デフォルト値は30、値の範囲は[0, 100]、動的に反映されます。
large_query_threshold
リクエストが大規模クエリとして判定される処理時間のしきい値です。
デフォルト値は5秒、値の範囲は[1ms, +∞]、動的に反映されます。
パラメータの確認および変更方法については、パラメータの設定を参照してください。
ログ診断
grep 'dump tenant info' observer.log* コマンドを使用すると、テナントのワーカースレッド数やリクエストキュー状況などを確認できます。例:
grep 'dump tenant info.tenant={id:1002' log/observer.log* | sed 's/,/,\n/g'
[2022-07-20 14:55:40.774143] INFO [SERVER.OMT] run1 (ob_multi_tenant.cpp:1993) [80700][MultiTenant][T0][Y0-0000000000000000-0-0] [lt=621]
dump tenant info(tenant={id:1002, //テナントID
tenant_meta:{unit:{tenant_id:1002, // tenant_metaはテナントメタデータで、unit構成情報、SuperBlock、create_statusが含まれる
unit_id:1001, //unit ID
has_memstore:true, // MemTableの有無
unit_status:"NORMAL", //unitの状態: UNIT_NORMAL/UNIT_MIGRATE_IN/UNIT_MIGRATE_OUT/UNIT_MARK_DELETING/UNIT_WAIT_GC_IN_OBSERVER/UNIT_DELETING_IN_OBSERVER/UNIT_ERROR_STAT
config:{unit_config_id:1003, // unitの構成ID
name:"2c2g", //unit構成名
resource:{min_cpu:2, //unit構成の最小CPU数
max_cpu:2, // unit構成の最大CPU数
memory_size:"1.5GB", // unit構成のメモリサイズ
log_disk_size:"5.4GB", // unit構成のログディスクサイズ
min_iops:20000, // unit構成の最小ディスクIOPS
max_iops:20000, // unit構成の最大ディスクIOPS
iops_weight:2}}, // unit構成のIOPS重み
mode:1, // CompatMode 0:MYSQL 1:ORACLE
create_timestamp:1658298418435426, // unitの作成時間
is_removed:false}, // このunitが削除フラグが付いているかどうか
super_block:{tenant_id:1002, // ObTenantSuperBlock情報
replay_start_point:ObLogCursor{file_id=1, // テナントslogのログ再生ポイント
log_id=440,
offset=343322},
ls_meta_entry:[140](ver=0, // テナントls metaのslogチェックポイントエントリポイント
mode=0,
seq=139),
tablet_meta_entry:[141](ver=0, // テナントtablet metaのslogチェックポイントのエントリポイント
mode=0,
seq=140)},
create_status:1}, // テナントの作成状態: CREATING = 0,CREATE_COMMIT=1,CREATE_ABORT=2,DELETING=3,DELETE_COMMIT=4
unit_min_cpu:"2.000000000000000000e+00", //最小CPUコア数、保証提供
unit_max_cpu:"2.000000000000000000e+00", //最大CPUコア数、制限上限
slice:"0.000000000000000000e+00",
slice_remain:"0.000000000000000000e+00",
token_cnt:8, //スケジューラーが割り当てたトークン数、1トークンは1ワーカースレッドに変換される
sug_token_cnt:8,
ass_token_cnt:8, //テナントが現在確認しているトークン数(token_cntに基づいて確認され、通常は両者が等しい)
lq_tokens:2, //Large Queryトークンの個数、token_cntに大規模リクエスト比率を掛けて設定する
used_lq_tokens:0, //現在LQ Tokenを保持しているWorker数
stopped:false, //テナントunitが削除中かどうか
idle_us:6076629, //1ラウンド(10秒)中のワーカースレッドのアイドル総時間、いわゆるアイドルは実際には待機キューの時間のみを統計する
recv_hp_rpc_cnt:1, //テナントが累計で受信した異なるレベルのRPCリクエスト数、hp(High)、np(Normal)、lp(Low)
recv_np_rpc_cnt:5,
recv_lp_rpc_cnt:0,
recv_mysql_cnt:24, //テナントが累計で受信したMySQLリクエスト数
recv_task_cnt:1, //テナントが累計で受信した内部タスク数
recv_large_req_cnt:0, //テナントが累計で予測した大規模リクエスト数、増加のみでリセットされない。実際には再試行時に増加する。
tt_large_quries:0, //テナントが累計で処理した大規模リクエスト数、増加のみでリセットされない。実際にはチェックポイントチェック時に増加する。
pop_normal_cnt:1024555,
actives:8, //アクティブなワーカースレッド数、通常はworkersと等しく、その差には以下が含まれる:テナントワーカースレッドキャッシュ + ワーカースレッドを持つ大規模リクエストキャッシュ
workers:8, //テナントが保持しているワーカースレッド数、実際にはworkers_というリストのサイズである。
nesting workers:7, //テナントが保持しているネストリクエスト専用スレッド数、合計7スレッドが7つのネストレベルに対応する。
lq waiting workers:0, //大規模クエリと判断され、スケジューリングを待つため一時停止されたワーカースレッド
req_queue:total_size=0 queue[0]=0 queue[1]=0 queue[2]=0 queue[3]=0 queue[4]=0 queue[5]=0 , //異なる優先順位のワークキュー、数字が小さいほど優先順位が高い
large queued:0, //現在予測されている大規模リクエストの個数
multi_level_queue:total_size=0 queue[0]=0 queue[1]=0 queue[2]=0 queue[3]=0 queue[4]=0 queue[5]=0 queue[6]=0 queue[7]=0 , //ネストリクエストを格納するワークキュー、1~7は7つのネストレベルに対応する(queue[0]は一時的に使用せず、queue[5]もinnersqlリクエストを格納する)。
recv_level_rpc_cnt:cnt[0]=0 cnt[1]=0 cnt[2]=0 cnt[3]=0 cnt[4]=0 cnt[5]=0 cnt[6]=0 cnt[7]=0 ,
group_map:group_id = 1, //group_mapの後には、各group_idに対応するグループのスレッドとキュー状況が続く
queue_size = 0, //groupキューのキュー状況
recv_req_cnt = 13526, //groupがキューにプッシュした累計リクエスト数
pop_req_cnt = 13526, //groupスレッドがポップした累計リクエスト数
token_cnt = 2, //groupに割り当てられたトークン数、1トークンは1ワーカースレッドに変換される
min_token_cnt = 2,
max_token_cnt = 2,
ass_token_cnt = 2 , //groupが現在確認しているトークン数(token_cntに基づいて確認され、通常は両者が等しい)
rpc_stat_info: pcode=0x150a:cnt=1489 pcode=0x150b:cnt=1091}) //テナントが一定期間内に受信した最多RPC pcode、統計周期は10秒、最多で上位5つを出力する