分割リージョン
TiDBで新しいテーブルを作成するたびに、デフォルトで1つのリージョンが分割され、そのテーブルのデータが格納されます。このデフォルトの動作は、TiDB構成ファイルのsplit-tableによって制御されます。このリージョン内のデータがデフォルトのリージョンサイズ制限を超えると、リージョンは2つに分割され始めます。
上記の場合、初期状態ではリージョンが1つしかないため、すべての書き込み要求はリージョンが配置されているTiKV上で発生します。新しく作成されたテーブルへの書き込みが大量に発生すると、ホットスポットが発生します。
上記シナリオにおけるホットスポット問題を解決するために、TiDBは事前分割機能を導入しました。この機能は、指定されたパラメータに従って特定のテーブルに対して複数のリージョンを事前に分割し、それらを各TiKVノードに分散させることができます。
注記:
この機能は、 TiDB Cloud StarterおよびTiDB Cloud Essentialインスタンスではご利用いただけません。
あらすじ
- SplitRegionStmt
- SplitSyntaxOption
- TableName
- PartitionNameList
- SplitOption
- RowValue
SplitRegionStmt ::=
"SPLIT" SplitSyntaxOption "TABLE" TableName PartitionNameList? ("INDEX" IndexName)? SplitOption
SplitSyntaxOption ::=
("REGION" "FOR")? "PARTITION"?
TableName ::=
(SchemaName ".")? Identifier
PartitionNameList ::=
"PARTITION" "(" PartitionName ("," PartitionName)* ")"
SplitOption ::=
("BETWEEN" RowValue "AND" RowValue "REGIONS" NUM
| "BY" RowValue ("," RowValue)* )
RowValue ::=
"(" ValuesOpt ")"
分割リージョンの使用
分割リージョン構文には2種類あります。
均等分割の構文:
SPLIT TABLE table_name [INDEX index_name] BETWEEN (lower_value) AND (upper_value) REGIONS region_numBETWEEN lower_value AND upper_value REGIONS region_num上限、下限、およびリージョン数を定義します。すると、現在の領域は、上限と下限の間で、指定された数の領域(region_numで指定)に均等に分割されます。不均等分割の構文:
SPLIT TABLE table_name [INDEX index_name] BY (value_list) [, (value_list)] ...BY value_list…、現在のリージョンを分割するための基準となる一連のポイントを手動で指定します。これは、データが不均一に分布しているシナリオに適しています。
次の例はSPLITステートメントの実行結果を示しています。
+--------------------+----------------------+
| TOTAL_SPLIT_REGION | SCATTER_FINISH_RATIO |
+--------------------+----------------------+
| 4 | 1.0 |
+--------------------+----------------------+
TOTAL_SPLIT_REGION: 新たに分割された地域の数。SCATTER_FINISH_RATIO: 新しく分割された領域の分散完了率。1.0すべての領域が分散されたことを意味します。0.5は、領域の半分のみが分散され、残りは分散中であることを意味します。
注記:
以下の2つのセッション変数は
SPLITステートメントの動作に影響を与える可能性があります。
tidb_wait_split_region_finish: リージョンの分散には時間がかかる場合があります。この期間は、PD スケジューリングと TiKV の負荷によって異なります。この変数は、SPLIT REGIONステートメントの実行時に、すべてのリージョンが分散されるまで結果をクライアントに返すかどうかを制御するために使用されます。値が1(デフォルト) に設定されている場合、TiDB は分散が完了した後にのみ結果を返します。値が0に設定されている場合、TiDB は分散状態に関係なく結果を返します。tidb_wait_split_region_timeout: この変数は、SPLIT REGIONステートメントの実行タイムアウトを秒単位で設定します。デフォルト値は 300 秒です。split操作が指定された時間内に完了しない場合、TiDB はタイムアウト エラーを返します。
分割テーブルリージョン
各テーブルの行データのキーは、 table_idとrow_idでエンコードされます。フォーマットは以下のとおりです。
t[table_id]_r[row_id]
例えば、 table_idが22で、 row_idが11の場合:
t22_r11
同じテーブル内の行データには同じtable_idがありますが、各行にはリージョン分割に使用できる固有のrow_idがあります。
均等に
row_idは整数であるため、分割するキーの値は、指定されたlower_value 、 upper_value 、およびregion_numに従って計算できます。TiDB は最初にステップ値 ( step = (upper_value - lower_value)/region_num ) を計算します。次に、 lower_valueとupper_valueの間で各「ステップ」ごとに均等に分割してregion_numで指定された数のリージョンを生成します。
例えば、テーブル t のキー範囲minInt64 ~ maxInt64から均等に分割された 16 個の領域を作成したい場合は、次のステートメントを使用できます。
SPLIT TABLE t BETWEEN (-9223372036854775808) AND (9223372036854775807) REGIONS 16;
このステートメントは、テーブル t を minInt64 と maxInt64 の間の 16 個の領域に分割します。指定された主キーの範囲が、例えば 0~1000000000 のように、指定された範囲よりも小さい場合は、minInt64 と maxInt64 の代わりにそれぞれ 0 と 1000000000 を使用して領域を分割できます。
SPLIT TABLE t BETWEEN (0) AND (1000000000) REGIONS 16;
不均等分割
既知のデータが不均等に分布している場合、リージョンをそれぞれキー範囲 -inf ~ 10000、10000 ~ 90000、90000 ~ +inf に分割したい場合は、以下に示すように固定点を設定することで実現できます。
SPLIT TABLE t BY (10000), (90000);
スプリットインデックスリージョン
テーブル内のインデックスデータのキーはtable_id 、 index_id 、およびインデックス列の値によってエンコードされます。形式は次のとおりです。
t[table_id]_i[index_id][index_value]
例えば、 table_idが 22、 index_idが 5、 index_valueが abc の場合:
t22_i5abc
1 つのテーブル内の同じインデックス データのtable_idとindex_idは同じです。インデックス領域を分割するには、 index_valueに基づいて領域を分割する必要があります。
均等に分割
インデックスを均等に分割する方法は、データを均等に分割する方法と同じです。ただし、 index_value整数にならない可能性があるため、ステップの値を計算するのはより複雑になります。
upperとlowerの値は、まずバイト配列にエンコードされます。 lowerとupperのバイト配列の最長共通プレフィックスを削除した後、 lowerとupperの最初の 8 バイトが uint64 形式に変換されます。次に、 step = (upper - lower)/numが計算されます。その後、計算されたステップはバイト配列にエンコードされ、インデックス分割のためにlowerとupperバイト配列の最長共通プレフィックスに追加されます。以下に例を示します。
idxインデックスの列が整数型の場合、次の SQL ステートメントを使用してインデックスデータを分割できます。
SPLIT TABLE t INDEX idx BETWEEN (-9223372036854775808) AND (9223372036854775807) REGIONS 16;
このステートメントは、テーブル t のインデックス idx のリージョンをminInt64からmaxInt64までの 16 の領域に分割します。
インデックス idx1 の列が varchar 型で、インデックスデータを接頭辞文字で分割したい場合。
SPLIT TABLE t INDEX idx1 BETWEEN ("a") AND ("z") REGIONS 25;
このステートメントは、インデックス idx1 を a~z の 25 個の領域に分割します。リージョン1 の範囲は[minIndexValue, b)です。リージョン2 の範囲は[b, c)です。…リージョン25 の範囲は[y, minIndexValue]です。 idxインデックスの場合、 a接頭辞を持つデータはリージョン1 に書き込まれ、 b接頭辞を持つデータはリージョン2 に書き込まれます。
上記の分割方法では、 yとzの接頭辞が付いたデータの両方がリージョン25 に書き込まれます。これは、上限がzではなく{ (ASCII でzの次の文字) であるためです。したがって、より正確な分割方法は次のようになります。
SPLIT TABLE t INDEX idx1 BETWEEN ("a") AND ("{") REGIONS 26;
このステートメントは、テーブルtのインデックス idx1 を{から 26 の領域に分割します。リージョン1 の範囲は[minIndexValue, b)です。リージョン2 の範囲は[b, c)です。…リージョン25 の範囲は[y, z)であり、リージョン26 の範囲は[z, maxIndexValue)です。
インデックスidx2の列がタイムスタンプ/日時などの時間型であり、インデックスリージョン を年ごとに分割したい場合:
SPLIT TABLE t INDEX idx2 BETWEEN ("2010-01-01 00:00:00") AND ("2020-01-01 00:00:00") REGIONS 10;
このステートメントは、テーブルidx2内のインデックスtのリージョンを2010-01-01 00:00:00から2020-01-01 00:00:00までの 10 個の領域に分割します。リージョン1 の範囲は[minIndexValue, 2011-01-01 00:00:00)です。リージョン2 の範囲は[2011-01-01 00:00:00, 2012-01-01 00:00:00)です。
インデックスリージョンを日ごとに分割したい場合は、次の例を参照してください。
SPLIT TABLE t INDEX idx2 BETWEEN ("2020-06-01 00:00:00") AND ("2020-07-01 00:00:00") REGIONS 30;
このステートメントは、テーブルidex2のインデックスt } の 2020 年 6 月のデータを 30 の領域に分割します。各リージョンは1 日を表します。
他のタイプのインデックス列に対するリージョン分割方法も同様です。
結合インデックスのデータリージョン分割の場合、唯一の違いは、複数の列の値を指定できる点です。
例えば、インデックスidx3 (a, b)には 2 つの列が含まれており、列aはタイムスタンプ型、列bは int 型です。列aに基づいて時間範囲を分割するだけであれば、単一列の時間インデックスを分割する SQL ステートメントを使用できます。この場合、列bとlower_valueでは列upper_velueでください。
SPLIT TABLE t INDEX idx3 BETWEEN ("2010-01-01 00:00:00") AND ("2020-01-01 00:00:00") REGIONS 10;
同じ時間範囲内で、列bに基づいてさらに分割を行いたい場合は、分割時に列bの値を指定するだけです。
SPLIT TABLE t INDEX idx3 BETWEEN ("2010-01-01 00:00:00", "a") AND ("2010-01-01 00:00:00", "z") REGIONS 10;
このステートメントは、列aと同じ時間接頭辞を持つ列bの値に基づいて、a~zの範囲にある10個の領域を分割します。列aに指定された値が異なる場合、列bの値は使用されない可能性があります。
テーブルの主キーが非クラスター化インデックスの場合、リージョンを分割するときにバックティック`を使用してPRIMARYキーワードをエスケープする必要があります。例えば:
SPLIT TABLE t INDEX `PRIMARY` BETWEEN (-9223372036854775808) AND (9223372036854775807) REGIONS 16;
不均等分割
インデックスデータは、指定されたインデックス値によって分割することもできます。
例えば、 idx4 (a,b)があり、列aは varchar 型、列bは timestamp 型です。
SPLIT TABLE t1 INDEX idx4 BY ("a", "2000-01-01 00:00:01"), ("b", "2019-04-17 14:26:19"), ("c", "");
このステートメントは、4つの領域を分割するための3つの値を指定します。各リージョンの範囲は次のとおりです。
region1 [ minIndexValue , ("a", "2000-01-01 00:00:01"))
region2 [("a", "2000-01-01 00:00:01") , ("b", "2019-04-17 14:26:19"))
region3 [("b", "2019-04-17 14:26:19") , ("c", "") )
region4 [("c", "") , maxIndexValue )
パーティション化されたテーブルの領域を分割する
パーティション化されたテーブルの領域分割は、通常のテーブルの領域分割と同じです。唯一の違いは、すべてのパーティションに対して同じ分割操作が実行される点です。
均等分割の構文:
SPLIT [PARTITION] TABLE t [PARTITION] [(partition_name_list...)] [INDEX index_name] BETWEEN (lower_value) AND (upper_value) REGIONS region_num不均等分割の構文:
SPLIT [PARTITION] TABLE table_name [PARTITION (partition_name_list...)] [INDEX index_name] BY (value_list) [, (value_list)] ...
パーティション化されたテーブルの分割領域の例
パーティションテーブル
tを作成します。ハッシュテーブルを 2 つのパーティションに分割して作成したいとします。例のステートメントは次のとおりです。CREATE TABLE t (a INT, b INT, INDEX idx(a)) PARTITION BY HASH(a) PARTITIONS 2;テーブル
tを作成すると、パーティションごとにリージョンが分割されます。このテーブルのリージョンを表示するには、SHOW TABLE REGIONS構文を使用します。SHOW TABLE t REGIONS;+-----------+-----------+---------+-----------+-----------------+------------------+------------+---------------+------------+----------------------+------------------+ | REGION_ID | START_KEY | END_KEY | LEADER_ID | LEADER_STORE_ID | PEERS | SCATTERING | WRITTEN_BYTES | READ_BYTES | APPROXIMATE_SIZE(MB) | APPROXIMATE_KEYS | +-----------+-----------+---------+-----------+-----------------+------------------+------------+---------------+------------+----------------------+------------------+ | 1978 | t_1400_ | t_1401_ | 1979 | 4 | 1979, 1980, 1981 | 0 | 0 | 0 | 1 | 0 | | 6 | t_1401_ | | 17 | 4 | 17, 18, 21 | 0 | 223 | 0 | 1 | 0 | +-----------+-----------+---------+-----------+-----------------+------------------+------------+---------------+------------+----------------------+------------------+SPLIT構文を使用して、パーティションごとにリージョンを分割します。たとえば、各パーティションの[0,10000]範囲のデータを4つの領域に分割したいとします。例のステートメントは次のとおりです。split partition table t between (0) and (10000) regions 4;上記の記述において、
0と10000はそれぞれ、散布したいホットスポットデータに対応する上側境界と下側境界のrow_idを表します。注記:
この例は、ホットスポット データが均等に分散されているシナリオにのみ適用されます。ホットスポット データが指定されたデータ範囲内で不均等に分散している場合は、パーティション化されたテーブルの領域を分割する不均等分割の構文を参照してください。
SHOW TABLE REGIONS構文を使用して、このテーブルのリージョンを再度表示します。このテーブルには10個のリージョンがあり、各パーティションには5つのリージョンがあり、そのうち4つが行データ、1つがインデックスデータであることがわかります。SHOW TABLE t REGIONS;+-----------+---------------+---------------+-----------+-----------------+------------------+------------+---------------+------------+----------------------+------------------+ | REGION_ID | START_KEY | END_KEY | LEADER_ID | LEADER_STORE_ID | PEERS | SCATTERING | WRITTEN_BYTES | READ_BYTES | APPROXIMATE_SIZE(MB) | APPROXIMATE_KEYS | +-----------+---------------+---------------+-----------+-----------------+------------------+------------+---------------+------------+----------------------+------------------+ | 1998 | t_1400_r | t_1400_r_2500 | 2001 | 5 | 2000, 2001, 2015 | 0 | 132 | 0 | 1 | 0 | | 2006 | t_1400_r_2500 | t_1400_r_5000 | 2016 | 1 | 2007, 2016, 2017 | 0 | 35 | 0 | 1 | 0 | | 2010 | t_1400_r_5000 | t_1400_r_7500 | 2012 | 2 | 2011, 2012, 2013 | 0 | 35 | 0 | 1 | 0 | | 1978 | t_1400_r_7500 | t_1401_ | 1979 | 4 | 1979, 1980, 1981 | 0 | 621 | 0 | 1 | 0 | | 1982 | t_1400_ | t_1400_r | 2014 | 3 | 1983, 1984, 2014 | 0 | 35 | 0 | 1 | 0 | | 1990 | t_1401_r | t_1401_r_2500 | 1992 | 2 | 1991, 1992, 2020 | 0 | 120 | 0 | 1 | 0 | | 1994 | t_1401_r_2500 | t_1401_r_5000 | 1997 | 5 | 1996, 1997, 2021 | 0 | 129 | 0 | 1 | 0 | | 2002 | t_1401_r_5000 | t_1401_r_7500 | 2003 | 4 | 2003, 2023, 2022 | 0 | 141 | 0 | 1 | 0 | | 6 | t_1401_r_7500 | | 17 | 4 | 17, 18, 21 | 0 | 601 | 0 | 1 | 0 | | 1986 | t_1401_ | t_1401_r | 1989 | 5 | 1989, 2018, 2019 | 0 | 123 | 0 | 1 | 0 | +-----------+---------------+---------------+-----------+-----------------+------------------+------------+---------------+------------+----------------------+------------------+各パーティションのインデックスごとにリージョンを分割することもできます。たとえば、
[1000,10000]インデックスのidx範囲を 2 つのリージョンに分割できます。例のステートメントは次のとおりです。SPLIT PARTITION TABLE t INDEX idx BETWEEN (1000) AND (10000) REGIONS 2;
単一パーティションの分割リージョンの例
分割するパーティションを指定できます。
パーティションテーブルを作成します。たとえば、3つのパーティションに分割された範囲パーティションテーブルを作成するとします。例となるステートメントは次のとおりです。
CREATE TABLE t ( a INT, b INT, INDEX idx(b)) PARTITION BY RANGE( a ) ( PARTITION p1 VALUES LESS THAN (10000), PARTITION p2 VALUES LESS THAN (20000), PARTITION p3 VALUES LESS THAN (MAXVALUE) );[0,10000]パーティションのp1範囲のデータを2つの領域に分割したいとします。例となるステートメントは次のとおりです。SPLIT PARTITION TABLE t PARTITION (p1) BETWEEN (0) AND (10000) REGIONS 2;[10000,20000]パーティションのp2範囲のデータを2つの領域に分割したいとします。例となるステートメントは次のとおりです。SPLIT PARTITION TABLE t PARTITION (p2) BETWEEN (10000) AND (20000) REGIONS 2;SHOW TABLE REGIONS構文を使用すると、このテーブルのリージョンを表示できます。SHOW TABLE t REGIONS;+-----------+----------------+----------------+-----------+-----------------+------------------+------------+---------------+------------+----------------------+------------------+ | REGION_ID | START_KEY | END_KEY | LEADER_ID | LEADER_STORE_ID | PEERS | SCATTERING | WRITTEN_BYTES | READ_BYTES | APPROXIMATE_SIZE(MB) | APPROXIMATE_KEYS | +-----------+----------------+----------------+-----------+-----------------+------------------+------------+---------------+------------+----------------------+------------------+ | 2040 | t_1406_ | t_1406_r_5000 | 2045 | 3 | 2043, 2045, 2044 | 0 | 0 | 0 | 1 | 0 | | 2032 | t_1406_r_5000 | t_1407_ | 2033 | 4 | 2033, 2034, 2035 | 0 | 0 | 0 | 1 | 0 | | 2046 | t_1407_ | t_1407_r_15000 | 2048 | 2 | 2047, 2048, 2050 | 0 | 35 | 0 | 1 | 0 | | 2036 | t_1407_r_15000 | t_1408_ | 2037 | 4 | 2037, 2038, 2039 | 0 | 0 | 0 | 1 | 0 | | 6 | t_1408_ | | 17 | 4 | 17, 18, 21 | 0 | 214 | 0 | 1 | 0 | +-----------+----------------+----------------+-----------+-----------------+------------------+------------+---------------+------------+----------------------+------------------+[0,20000]およびidxインデックスのp1p2範囲を 2 つの領域に分割したいとします。例となるステートメントは次のとおりです。SPLIT PARTITION TABLE t PARTITION (p1,p2) INDEX idx BETWEEN (0) AND (20000) REGIONS 2;
分割前領域
AUTO_RANDOMまたはSHARD_ROW_ID_BITS属性を使用してテーブルを作成する場合、テーブル作成直後にテーブルをリージョンに均等に事前分割したい場合はPRE_SPLIT_REGIONSオプションを指定することもできます。テーブルの事前分割リージョンの数は2^(PRE_SPLIT_REGIONS)です。
注記:
PRE_SPLIT_REGIONSの値は、SHARD_ROW_ID_BITSまたはAUTO_RANDOMの値以下でなければなりません。
tidb_scatter_regionグローバル変数はPRE_SPLIT_REGIONSの動作に影響します。この変数は、テーブル作成後に結果を返す前に、リージョンが事前に分割され分散されるまで待機するかどうかを制御します。テーブル作成後に書き込みが集中する場合は、この変数の値をglobalに設定する必要があります。そうすると、TiDB はクラスタ全体のデータ分布に従って、新しく作成されたテーブルのリージョンを分散します。そうでない場合、TiDB は分散が完了する前にデータを書き込み、書き込みパフォーマンスに大きな影響を与えます。
pre_split_regionsの例
CREATE TABLE t (a INT, b INT, INDEX idx1(a)) SHARD_ROW_ID_BITS = 4 PRE_SPLIT_REGIONS=2;
テーブルを作成した後、このステートメントはテーブル t の4 + 1領域を分割します。 4 (2^2)領域はテーブルの行データを保存するのに使用され、1 つのリージョンはidx1のインデックス データを保存するために使用されます。
4つの表領域の範囲は以下のとおりです。
region1: [ -inf , 1<<61 )
region2: [ 1<<61 , 2<<61 )
region3: [ 2<<61 , 3<<61 )
region4: [ 3<<61 , +inf )
注記:
Split リージョンステートメントによって分割されたリージョンは、PD のリージョンの統合スケジューラーによって制御されます。 PD が新しく分割されたリージョンをすぐに再マージしないようにするには、テーブル属性プロパティ動的に変更するに変更するリージョンマージ機能に関連する構成アイテムを使用する必要があります。
MySQLとの互換性
このステートメントは、MySQL構文に対するTiDBの拡張機能です。