重要
このページは英語版のページを機械翻訳しています。原文はこちらからご覧ください。

TiDBメモリ制御

現在、TiDBは、単一のSQLクエリのメモリクォータを追跡し、メモリ使用量が特定のしきい値を超えたときにOOM(メモリ不足)を防止するか、OOMのトラブルシューティングを行うためのアクションを実行できます。 TiDB構成ファイルでは、メモリクォータがしきい値を超えたときのTiDBの動作を制御するために、以下のオプションを構成できます。

# Valid options: ["log", "cancel"]
oom-action = "cancel"
  • 上記の構成アイテムが「ログ」を使用している場合、単一のSQLクエリのメモリクォータがtidb_mem_quota_query変数によって制御されるしきい値を超えると、TiDBはログのエントリを出力します。その後、SQLクエリは引き続き実行されます。 OOMが発生した場合、対応するSQLクエリをログで見つけることができます。
  • 上記の構成項目で「キャンセル」を使用している場合、1つのSQLクエリのメモリクォータがしきい値を超えると、TiDBはSQLクエリの実行をすぐに停止し、クライアントにエラーを返します。エラー情報は、SQL実行プロセスで多くのメモリを消費する各物理実行演算子のメモリ使用量を明確に示しています。

クエリのメモリクォータを構成する

構成ファイルでは、クエリごとにデフォルトのメモリクォータを設定できます。次の例では、32GBに設定しています。

mem-quota-query = 34359738368

さらに、次のセッション変数を使用して、クエリのメモリクォータを制御できます。通常、構成する必要があるのはtidb_mem_quota_queryだけです。他の変数は、ほとんどのユーザーが気にする必要のない高度な構成に使用されます。

変数名説明単位デフォルト値
tidb_mem_quota_queryクエリのメモリクォータを制御するバイト1 << 30(1 GB)
tidb_mem_quota_hashjoin「HashJoinExec」のメモリクォータを制御するバイト32 << 30
tidb_mem_quota_mergejoin「MergeJoinExec」のメモリクォータを制御するバイト32 << 30
tidb_mem_quota_sort「SortExec」のメモリクォータを制御するバイト32 << 30
tidb_mem_quota_topn「TopNExec」のメモリクォータを制御するバイト32 << 30
tidb_mem_quota_indexlookupreader「IndexLookUpExecutor」のメモリクォータを制御しますバイト32 << 30
tidb_mem_quota_indexlookupjoin「IndexLookUpJoin」のメモリクォータを制御しますバイト32 << 30
tidb_mem_quota_nestedloopapply「NestedLoopApplyExec」のメモリクォータを制御しますバイト32 << 30

いくつかの使用例:

-- Set the threshold value of memory quota for a single SQL query to 8GB:
set @@tidb_mem_quota_query = 8 << 30;
-- Set the threshold value of memory quota for a single SQL query to 8MB:
set @@tidb_mem_quota_query = 8 << 20;
-- Set the threshold value of memory quota for a single SQL query to 8KB:
set @@tidb_mem_quota_query = 8 << 10;

tidb-serverインスタンスのメモリ使用量のしきい値を構成します

TiDB構成ファイルでは、 server-memory-quotaを構成することにより、tidb-serverインスタンスのメモリ使用量のしきい値を設定できます。

次の例では、tidb-serverインスタンスの合計メモリ使用量を32GBに設定します。

[performance]
server-memory-quota = 34359738368

この構成では、tidb-serverインスタンスのメモリ使用量が32 GBに達すると、メモリ使用量が32 GBを下回るまで、インスタンスは実行中のSQLステートメントをランダムに強制終了し始めます。強制終了するSQL操作は、クライアントにOut Of Global Memory Limit!エラーメッセージを返します。

警告
  • server-memory-quotaはまだ実験的機能です。実稼働環境で使用することはお勧めしません
  • デフォルト値のserver-memory-quota0です。これは、メモリ制限がないことを意味します。

過度のメモリ使用量のアラームをトリガーします

デフォルトの構成では、マシンのメモリ使用量が合計メモリの80%に達すると、tidb-serverインスタンスはアラームログを出力し、関連するステータスファイルを記録します。 memory-usage-alarm-ratioを設定することにより、メモリ使用率のしきい値を設定できます。詳細なアラームルールについては、 memory-usage-alarm-ratioの説明を参照してください。

アラームが1回トリガーされた後、メモリ使用率が10秒を超えてしきい値を下回り、再びしきい値に達した場合にのみ、アラームが再度トリガーされることに注意してください。さらに、アラームによって生成された過剰なステータスファイルの保存を回避するために、現在、TiDBは最近の5つのアラーム中に生成されたステータスファイルのみを保持します。

次の例では、アラームをトリガーするメモリを大量に消費するSQLステートメントを作成します。

  1. memory-usage-alarm-ratioから0.8に設定:

    mem-quota-query = 34359738368  // Increases the memory limit of each query to construct SQL statements that take up larger memory.
    [performance]
    memory-usage-alarm-ratio = 0.8
    
  2. CREATE TABLE t(a int);を実行し、1000行のデータを挿入します。

  3. select * from t t1 join t t2 join t t3 order by t1.aを実行します。このSQLステートメントは10億レコードを出力しますが、これは大量のメモリを消費するため、アラームをトリガーします。

  4. 合計システムメモリ、現在のシステムメモリ使用量、tidb-serverインスタンスのメモリ使用量、およびステータスファイルのディレクトリを記録するtidb.logのファイルを確認します。

    [2020/11/30 15:25:17.252 +08:00] [WARN] [memory_usage_alarm.go:141] ["tidb-server has the risk of OOM. Running SQLs and heap profile will be recorded in record path"] ["is server-memory-quota set"=false] ["system memory total"=33682427904] ["system memory usage"=27142864896] ["tidb-server memory usage"=22417922896] [memory-usage-alarm-ratio=0.8] ["record path"="/tmp/1000_tidb/MC4wLjAuMDo0MDAwLzAuMC4wLjA6MTAwODA=/tmp-storage/record"]
    

    上記のサンプルログファイルのフィールドは次のとおりです。

    • is server-memory-quota setは、 server-memory-quotaが設定されているかどうかを示します。
    • system memory totalは、現在のシステムの合計メモリを示します。
    • system memory usageは、現在のシステムメモリ使用量を示します。
    • tidb-server memory usageは、tidb-serverインスタンスのメモリ使用量を示します。
    • memory-usage-alarm-ratiomemory-usage-alarm-ratioの値を示します。
    • record pathはステータスファイルのディレクトリを示します。
  5. ステータスファイルのディレクトリ(上記の例では、ディレクトリは/tmp/1000_tidb/MC4wLjAuMDo0MDAwLzAuMC4wLjA6MTAwODA=/tmp-storage/record )に、 goroutinue 、およびheapを含む一連のファイルが表示されrunning_sql 。これらの3つのファイルには、ステータスファイルがログに記録される時刻の接尾辞が付いています。これらはそれぞれ、ゴルーチンスタック情報、ヒープメモリの使用状況、およびアラームがトリガーされたときの実行中のSQL情報を記録します。 running_sqlのログ内容の形式については、 expensive-queriesを参照してください。

tidb-serverの他のメモリ制御動作

フロー制御

  • TiDBは、データを読み取るオペレーターの動的メモリ制御をサポートしています。デフォルトでは、この演算子はtidb_distsql_scan_concurrencyがデータの読み取りを許可するスレッドの最大数を使用します。 1回のSQL実行のメモリ使用量が毎回tidb_mem_quota_queryを超えると、データを読み取るオペレーターが1つのスレッドを停止します。

  • このフロー制御の動作は、システム変数tidb_enable_rate_limit_actionによって制御されます。

  • フロー制御動作がトリガーされると、TiDBはキーワードmemory exceeds quota, destroy one token nowを含むログを出力します。

ディスクの流出

TiDBは、実行オペレーターのディスクスピルをサポートしています。 SQL実行のメモリ使用量がメモリクォータを超えると、tidb-serverは実行演算子の中間データをディスクにスピルして、メモリの負荷を軽減できます。ディスクスピルをサポートする演算子には、Sort、MergeJoin、HashJoin、およびHashAggが含まれます。

  • ディスクtmp-storage-quota mem-quota-query tmp-storage-pathによって共同で制御されoom-use-tmp-storage
  • ディスクスピルがトリガーされると、TiDBはキーワードmemory exceeds quota, spill to disk nowまたはmemory exceeds quota, set aggregate mode to spill-modeを含むログを出力します。
  • Sort、MergeJoin、およびHashJoin演算子のディスクスピルはv4.0.0で導入されました。 HashAggオペレーターのディスクスピルはv5.2.0で導入されました。
  • Sort、MergeJoin、またはHashJoinを含むSQL実行によってOOMが発生すると、TiDBはデフォルトでディスクスピルをトリガーします。 HashAggを含むSQL実行がOOMを引き起こす場合、TiDBはデフォルトでディスクスピルをトリガーしません。 HashAggのディスクスピルをトリガーするようにシステム変数tidb_executor_concurrency = 1を構成できます。

ノート:

HashAggのディスクスピルは、 DISTINCT集約関数を含むSQL実行をサポートしていません。 DISTINCT集計関数を含むSQL実行が大量のメモリを使用する場合、ディスクスピルは適用されません。

次の例では、メモリを消費するSQLステートメントを使用して、HashAggのディスクスピル機能を示しています。

  1. SQLステートメントのメモリクォータを1GB(デフォルトでは1 GB)に構成します。

    set tidb_mem_quota_query = 1 << 30;
    
  2. 単一のテーブルCREATE TABLE t(a int);を作成し、256行の異なるデータを挿入します。

  3. 次のSQLステートメントを実行します。

    [tidb]> explain analyze select /*+ HASH_AGG() */ count(*) from t t1 join t t2 join t t3 group by t1.a, t2.a, t3.a;
    

    このSQLステートメントを実行するとメモリを大量に消費するため、次の「メモリ不足クォータ」エラーメッセージが返されます。

    ERROR 1105 (HY000): Out Of Memory Quota![conn_id=3]
    
  4. システム変数tidb_executor_concurrencyを1に構成します。この構成では、メモリが不足すると、HashAggは自動的にディスクスピルをトリガーしようとします。

    set tidb_executor_concurrency = 1;
    
  5. 同じSQLステートメントを実行します。今回は、ステートメントが正常に実行され、エラーメッセージが返されないことがわかります。次の詳細な実行プランから、HashAggが600MBのハードディスク領域を使用していることがわかります。

    [tidb]> explain analyze select /*+ HASH_AGG() */ count(*) from t t1 join t t2 join t t3 group by t1.a, t2.a, t3.a;
    
    +---------------------------------+-------------+----------+-----------+---------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------+-----------------------------------------------------------------+-----------+----------+
    | id                              | estRows     | actRows  | task      | access object | execution info                                                                                                                                                      | operator info                                                   | memory    | disk     |
    +---------------------------------+-------------+----------+-----------+---------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------+-----------------------------------------------------------------+-----------+----------+
    | HashAgg_11                      | 204.80      | 16777216 | root      |               | time:1m37.4s, loops:16385                                                                                                                                           | group by:test.t.a, test.t.a, test.t.a, funcs:count(1)->Column#7 | 1.13 GB   | 600.0 MB |
    | └─HashJoin_12                   | 16777216.00 | 16777216 | root      |               | time:21.5s, loops:16385, build_hash_table:{total:267.2µs, fetch:228.9µs, build:38.2µs}, probe:{concurrency:1, total:35s, max:35s, probe:35s, fetch:962.2µs}         | CARTESIAN inner join                                            | 8.23 KB   | 4 KB     |
    |   ├─TableReader_21(Build)       | 256.00      | 256      | root      |               | time:87.2µs, loops:2, cop_task: {num: 1, max: 150µs, proc_keys: 0, rpc_num: 1, rpc_time: 145.1µs, copr_cache_hit_ratio: 0.00}                                       | data:TableFullScan_20                                           | 885 Bytes | N/A      |
    |   │ └─TableFullScan_20          | 256.00      | 256      | cop[tikv] | table:t3      | tikv_task:{time:23.2µs, loops:256}                                                                                                                                  | keep order:false, stats:pseudo                                  | N/A       | N/A      |
    |   └─HashJoin_14(Probe)          | 65536.00    | 65536    | root      |               | time:728.1µs, loops:65, build_hash_table:{total:307.5µs, fetch:277.6µs, build:29.9µs}, probe:{concurrency:1, total:34.3s, max:34.3s, probe:34.3s, fetch:278µs}      | CARTESIAN inner join                                            | 8.23 KB   | 4 KB     |
    |     ├─TableReader_19(Build)     | 256.00      | 256      | root      |               | time:126.2µs, loops:2, cop_task: {num: 1, max: 308.4µs, proc_keys: 0, rpc_num: 1, rpc_time: 295.3µs, copr_cache_hit_ratio: 0.00}                                    | data:TableFullScan_18                                           | 885 Bytes | N/A      |
    |     │ └─TableFullScan_18        | 256.00      | 256      | cop[tikv] | table:t2      | tikv_task:{time:79.2µs, loops:256}                                                                                                                                  | keep order:false, stats:pseudo                                  | N/A       | N/A      |
    |     └─TableReader_17(Probe)     | 256.00      | 256      | root      |               | time:211.1µs, loops:2, cop_task: {num: 1, max: 295.5µs, proc_keys: 0, rpc_num: 1, rpc_time: 279.7µs, copr_cache_hit_ratio: 0.00}                                    | data:TableFullScan_16                                           | 885 Bytes | N/A      |
    |       └─TableFullScan_16        | 256.00      | 256      | cop[tikv] | table:t1      | tikv_task:{time:71.4µs, loops:256}                                                                                                                                  | keep order:false, stats:pseudo                                  | N/A       | N/A      |
    +---------------------------------+-------------+----------+-----------+---------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------+-----------------------------------------------------------------+-----------+----------+
    9 rows in set (1 min 37.428 sec)