バリアント
VARIANT
概要
VARIANT型は半構造化JSONデータを格納します。異なるプリミティブ型(整数、文字列、ブール値など)、一次元配列、ネストされたオブジェクトを含むことができます。書き込み時に、DorisはJSONパスに基づいてサブパスの構造と型を推論し、頻繁に使用されるパスに対してSubcolumnizationを実行し、柔軟性とパフォーマンスの両方を実現するために独立したカラム型サブカラムとして公開します。
VARIANTは、ドキュメントの形状が時間の経過とともに変化するが、クエリが依然として少数のホットパスに焦点を当てている場合に適しています。
- ホットパスはSubcolumnizationに参加するため、カラム型パフォーマンス、ファイルプルーニング、ベクトル化実行の恩恵を受けます。
- キーパスはパスレベルのインデックス、全文検索を使用でき、Dorisのスパースインデックスプルーニングの恩恵を依然として受けることができます。
- ワイドカラム最適化により、10k規模のサブカラムで自動Subcolumnizationが実用的になります。Subcolumnizationに参加するパスが10,000に近づくと、ハードウェア要件が急速に上昇するため、通常DOCモードがより安全な開始点です。
デフォルト動作、スパースカラム、DOCモード、Schema Templateのいずれかを選択する必要がある場合は、Variant Workload Guideから始めてください。このページは構文、型ルール、インデックス、制限、設定のリファレンスです。
VARIANTの使用
テーブル作成構文
テーブル作成時にVARIANTカラムを宣言します:
CREATE TABLE IF NOT EXISTS ${table_name} (
k BIGINT,
v VARIANT
)
PROPERTIES("replication_num" = "1");
Schema Template(「拡張型」を参照)で特定のパスを制約する:
CREATE TABLE IF NOT EXISTS ${table_name} (
k BIGINT,
v VARIANT <
'id' : INT, -- restrict path id to INT
'message*' : STRING, -- restrict message* prefix to STRING
'tags*' : ARRAY<TEXT> -- restrict tags* prefix to ARRAY<TEXT>
>
)
PROPERTIES("replication_num" = "1");
クエリ構文
-- Access nested fields (returns VARIANT; explicit or implicit CAST is required for aggregation/comparison)
SELECT v['properties']['title'] FROM ${table_name};
-- CAST to a concrete type before aggregation
SELECT CAST(v['properties']['title'] AS STRING) AS title
FROM ${table_name}
GROUP BY title;
-- Query arrays
SELECT *
FROM ${table_name}
WHERE ARRAY_CONTAINS(CAST(v['tags'] AS ARRAY<TEXT>), 'Doris');
プリミティブ型
VARIANTはサブカラムの型を自動的に推論します。サポートされる型には以下が含まれます:
| サポートされる型 |
| TinyInt |
| NULL (JSON nullと同等) |
| BigInt (64 bit) Double |
| String (Text) |
| Jsonb |
| Variant (ネストされたオブジェクト) |
| Array<T> (1次元のみ) |
シンプルなINSERTの例:
INSERT INTO vartab VALUES
(1, 'null'),
(2, NULL),
(3, 'true'),
(4, '-17'),
(5, '123.12'),
(6, '1.912'),
(7, '"A quote"'),
(8, '[-1, 12, false]'),
(9, '{ "x": "abc", "y": false, "z": 10 }'),
(10, '"2021-01-01"');
ヒント: 日付/時刻などの非標準JSON型は、Schema Templateが提供されない限り文字列として保存されます。計算効率を向上させるには、それらを静的カラムに抽出するか、Schema Templateを介して型を宣言することを検討してください。
拡張型 (Schema Template)
プリミティブ型に加えて、VARIANTはSchema Templateを介して以下の拡張型をサポートします:
- Number (拡張)
- Decimal: Decimal32 / Decimal64 / Decimal128 / Decimal256
- LargeInt
- Datetime
- Timestamptz
- Date
- IPV4 / IPV6
- Boolean
- ARRAY<T> (Tは上記のいずれでも可、1次元のみ)
注意: 事前定義されたSchemaはテーブル作成時にのみ指定できます。ALTERは現在サポートされていません(将来のバージョンでは新しいサブカラム定義の追加をサポートする可能性がありますが、既存のサブカラム型の変更はサポートされません)。
例:
CREATE TABLE test_var_schema (
id BIGINT NOT NULL,
v1 VARIANT<
'large_int_val': LARGEINT,
'string_val': STRING,
'decimal_val': DECIMAL(38, 9),
'datetime_val': DATETIME,
'tz_val': TIMESTAMPTZ,
'ip_val': IPV4
> NULL
)
PROPERTIES ("replication_num" = "1");
INSERT INTO test_var_schema VALUES (1, '{
"large_int_val" : "123222222222222222222222",
"string_val" : "Hello World",
"decimal_val" : 1.11111111,
"datetime_val" : "2025-05-16 11:11:11",
"tz_val" : "2025-05-16 11:11:11+08:00",
"ip_val" : "127.0.0.1"
}');
SELECT variant_type(v1) FROM test_var_schema;
+---------------------------------------------------------------------------------------------------------------------------------------------------+
| variant_type(v1) |
+---------------------------------------------------------------------------------------------------------------------------------------------------+
| {"datetime_val":"datetimev2","decimal_val":"decimal128i","ip_val":"ipv4","large_int_val":"largeint","string_val":"string","tz_val":"timestamptz"} |
+---------------------------------------------------------------------------------------------------------------------------------------------------+
{"date": 2020-01-01}と{"ip": 127.0.0.1}は無効なJSONテキストです。正しい形式は{"date": "2020-01-01"}と{"ip": "127.0.0.1"}です。
Schema Templateが指定されると、JSON値が宣言された型と競合し、変換できない場合、NULLとして保存されます。例えば:
INSERT INTO test_var_schema VALUES (1, '{
"decimal_val" : "1.11111111",
"ip_val" : "127.xxxxxx.xxxx",
"large_int_val" : "aaabbccc"
}');
-- Only decimal_val remains
SELECT * FROM test_var_schema;
+------+-----------------------------+
| id | v1 |
+------+-----------------------------+
| 1 | {"decimal_val":1.111111110} |
+------+-----------------------------+
スキーマは永続化されたストレージタイプのみを指定します。クエリ実行時には、実効的なタイプは実行時の実際のデータに依存します:
-- At runtime v['a'] may still be STRING
SELECT variant_type(CAST('{"a" : "12345"}' AS VARIANT<'a' : INT>)['a']);
ワイルドカードマッチングと順序:
CREATE TABLE test_var_schema (
id BIGINT NOT NULL,
v1 VARIANT<
'enumString*' : STRING,
'enum*' : ARRAY<TEXT>,
'ip*' : IPV6
> NULL
)
PROPERTIES ("replication_num" = "1");
-- If enumString1 matches both patterns, the first matching pattern in definition order (STRING) is used
カラム名に * が含まれており、プレフィックスワイルドカードとしてではなく、リテラル名として一致させたい場合は、次のようにします:
v1 VARIANT<
MATCH_NAME 'enumString*' : STRING
> NULL
マッチしたサブパスはデフォルトでSubcolumnizationに参加し、カラムとして公開されます。マッチするパスが多すぎて過剰なカラムが生成される場合は、variant_enable_typed_paths_to_sparseの有効化を検討してください(「Configuration」を参照)。
型の競合とプロモーションルール
同じパス上に互換性のない型が現れた場合(例:同じフィールドが整数と文字列の両方として現れる)、情報損失を避けるために型はJSONBにプロモーションされます:
{"a" : 12345678}
{"a" : "HelloWorld"}
-- a will be promoted to JSONB
プロモーションルール:
| Source type | Current type | Final type |
|---|---|---|
TinyInt | BigInt | BigInt |
TinyInt | Double | Double |
TinyInt | String | JSONB |
TinyInt | Array | JSONB |
BigInt | Double | JSONB |
BigInt | String | JSONB |
BigInt | Array | JSONB |
Double | String | JSONB |
Double | Array | JSONB |
Array<Double> | Array<String> | Array<Jsonb> |
厳密な型(安定したインデックス作成とストレージのため)が必要な場合は、Schema Templateで宣言してください。
Variantインデックス
インデックスの選択
VARIANTはサブパスでBloomFilterとInverted Indexをサポートしています。
- 高カーディナリティの等価/INフィルター: BloomFilterを推奨(より疎なインデックス、優れた書き込みパフォーマンス)。
- トークン化/フレーズ/範囲検索: Inverted Indexを使用し、適切な
parser/analyzerプロパティを設定してください。
...
PROPERTIES("replication_num" = "1", "bloom_filter_columns" = "v");
-- Use BloomFilter for equality/IN filters
SELECT * FROM tbl WHERE v['id'] = 12345678;
SELECT * FROM tbl WHERE v['id'] IN (1, 2, 3);
VARIANT列に転置インデックスが作成されると、すべてのサブパスは同じインデックスプロパティ(例:parser)を継承します。
CREATE TABLE IF NOT EXISTS tbl (
k BIGINT,
v VARIANT,
INDEX idx_v(v) USING INVERTED PROPERTIES("parser" = "english")
);
-- All subpaths inherit the english parser
SELECT * FROM tbl WHERE v['id_1'] MATCH 'Doris';
SELECT * FROM tbl WHERE v['id_2'] MATCH 'Apache';
サブパスによるインデックス
3.1.x/4.0以降では、特定のVARIANTサブパスに対してインデックスプロパティを指定でき、同じパスに対してトークン化された転置インデックスとトークン化されていない転置インデックスの両方を設定することも可能です。パス固有のインデックスでは、Schema Templateを介してパスタイプを宣言する必要があります。
-- Common properties: field_pattern (target path), analyzer, parser, support_phrase, etc.
CREATE TABLE IF NOT EXISTS tbl (
k BIGINT,
v VARIANT<'content' : STRING>,
INDEX idx_tokenized(v) USING INVERTED PROPERTIES("parser" = "english", "field_pattern" = "content"),
INDEX idx_v(v) USING INVERTED PROPERTIES("field_pattern" = "content")
);
-- v.content has both tokenized and non-tokenized inverted indexes
SELECT * FROM tbl WHERE v['content'] MATCH 'Doris';
SELECT * FROM tbl WHERE v['content'] = 'Doris';
ワイルドカードパスインデックス:
CREATE TABLE IF NOT EXISTS tbl (
k BIGINT,
v VARIANT<'pattern_*' : STRING>,
INDEX idx_tokenized(v) USING INVERTED PROPERTIES("parser" = "english", "field_pattern" = "pattern_*"),
INDEX idx_v(v) USING INVERTED -- global non-tokenized inverted index
);
SELECT * FROM tbl WHERE v['pattern_1'] MATCH 'Doris';
SELECT * FROM tbl WHERE v['pattern_1'] = 'Doris';
注意: 2.1.7+では InvertedIndex V2 プロパティのみをサポートしています(ファイル数が少なく、書き込み IOPS が低い。分散ストレージ/コンピュートに適している)。2.1.8+では offline Build Index が削除されています。
インデックスが機能しない場合
-
型変更によるインデックス損失: サブパスが非互換の型に変更された場合(例: INT → JSONB)、インデックスが失われます。Schema Template を使用して型とインデックスを固定することで修正できます。
-
クエリ型の不一致:
-- v['id'] is actually STRING; using INT equality causes index not to be used
SELECT * FROM tbl WHERE v['id'] = 123456; -
インデックスの設定ミス: インデックスはサブパスに適用され、VARIANT列全体には適用されません。
-- VARIANT itself cannot be indexed as a whole
SELECT * FROM tbl WHERE v MATCH 'Doris';
-- If whole-JSON search is needed, store a duplicate STRING column and index it
CREATE TABLE IF NOT EXISTS tbl (
k BIGINT,
v VARIANT,
v_str STRING,
INDEX idx_v_str(v_str) USING INVERTED PROPERTIES("parser" = "english")
);
SELECT * FROM tbl WHERE v_str MATCH 'Doris';
Variant Nested Group
Variant Nested Groupは、VARIANT内のarray<object>値(トップレベルのオブジェクト配列を含む)に対する4.1.xのストレージおよび検索機能です。有効にすると、Dorisは配列要素ごとにネストされたオブジェクトのサブパスを整理するため、ログ、トレース、イベント詳細、およびオブジェクト配列を含むその他のレコードに有用です。
主要なクエリセマンティクスは同一要素マッチングです。行に複数のitems要素が含まれている場合、msg:hello AND title:newsは両方の述語が同じ配列要素内でtrueである場合にのみマッチし、異なる要素間での偶発的なマッチを回避します。
Nested Groupを有効にする
新しいテーブルの場合、それが必要なVARIANTカラムでNested Groupを明示的に有効にします:
CREATE TABLE variant_nested_group_tbl (
id INT,
data VARIANT<PROPERTIES("variant_enable_nested_group" = "true")>,
INDEX idx_data (data) USING INVERTED PROPERTIES("parser" = "english")
) ENGINE=OLAP
DUPLICATE KEY(id)
DISTRIBUTED BY HASH(id) BUCKETS 1
PROPERTIES (
"replication_num" = "1"
);
テーブルを作成する前にセッションのデフォルトを設定することもできます:
SET default_variant_enable_nested_group = true;
column プロパティとセッション変数の両方が設定されている場合、column プロパティが優先されます。このプロパティはテーブル作成時にのみ指定でき、ALTER で変更することはできません。
ネストした配列の書き込みと読み込み
Nested Group は、オブジェクト内の配列パスとトップレベル配列をサポートします:
INSERT INTO variant_nested_group_tbl VALUES
(1, '{"items":[{"msg":"hello","title":"news"},{"msg":"foo","title":"bar"}]}'),
(2, '{"items":[{"msg":"hello"},{"title":"news"}]}'),
(3, '[{"msg":"hello","title":"news"}]'),
(4, NULL);
-- Read an array path inside an object
SELECT id, CAST(data['items'] AS STRING)
FROM variant_nested_group_tbl
ORDER BY id;
-- A top-level array is still read through the VARIANT column itself
SELECT id, CAST(data AS STRING)
FROM variant_nested_group_tbl
WHERE id = 3;
ネストされた検索
VARIANT列に転置インデックスを作成した後、同じネストされた配列要素内で述語を結合するためにSEARCH DSLでNESTED(path, predicate)を使用します:
-- Same-element match under object field items. This matches only row 1.
SELECT id
FROM variant_nested_group_tbl
WHERE SEARCH('NESTED(data.items, msg:hello AND title:news)')
ORDER BY id;
-- Same-element match on a top-level array. This matches only row 3.
SELECT id
FROM variant_nested_group_tbl
WHERE SEARCH('NESTED(data, msg:hello AND title:news)')
ORDER BY id;
-- Single-field match
SELECT id
FROM variant_nested_group_tbl
WHERE SEARCH('NESTED(data.items, msg:hello)')
ORDER BY id;
NESTED内のフィールドは、ネストされたパスに相対的でなければなりません。例えば、NESTED(data.items, data.items.msg:hello)ではなく、NESTED(data.items, msg:hello)を使用してください。内部述語は通常のSEARCH DSL用語、フレーズ、ANY/ALL、およびAND/OR/NOTをサポートしますが、ネストしたNESTED()呼び出しはサポートされていません。
完全なトップレベル配列の例
VARIANTカラム自体がオブジェクトのトップレベル配列の場合、NESTEDパスとしてカラム名を直接使用してください。以下の例では、行2はmsg=helloとtitle=newsが異なる配列要素にあるため、同一要素のANDクエリは行2にマッチしません:
CREATE TABLE variant_top_array_tbl (
id INT,
data VARIANT<PROPERTIES("variant_enable_nested_group" = "true")>,
INDEX idx_data (data) USING INVERTED PROPERTIES("parser" = "english")
) ENGINE=OLAP
DUPLICATE KEY(id)
DISTRIBUTED BY HASH(id) BUCKETS 1
PROPERTIES (
"replication_num" = "1"
);
INSERT INTO variant_top_array_tbl VALUES
(1, '[{"msg":"hello","title":"news"},{"msg":"foo","title":"bar"}]'),
(2, '[{"msg":"hello"},{"title":"news"}]'),
(3, '[{"msg":"hello","title":"sports"}]');
-- Read the top-level array
SELECT id, CAST(data AS STRING)
FROM variant_top_array_tbl
ORDER BY id;
-- Same-element match for msg=hello and title=news. This matches only id=1.
SELECT id
FROM variant_top_array_tbl
WHERE SEARCH('NESTED(data, msg:hello AND title:news)')
ORDER BY id;
-- Single-field match for msg=hello. This matches id=1, 2, and 3.
SELECT id
FROM variant_top_array_tbl
WHERE SEARCH('NESTED(data, msg:hello)')
ORDER BY id;
explode を使用した配列の展開
射影やフィルタリングのために配列要素を行に展開するには、LATERAL VIEW explode を使用します。Nested Group テーブルでは、非トップレベル配列とトップレベル配列は explode に渡される式のみが異なります:
- 非トップレベル配列:
data['items']などの配列パスを渡す - トップレベル配列:
dataなどのVARIANTカラム自体を渡す
-- Non-top-level array: expand elements under object field items
SELECT
id,
CAST(e['msg'] AS STRING) AS msg,
CAST(e['title'] AS STRING) AS title
FROM variant_nested_group_tbl
LATERAL VIEW explode(data['items']) tmp AS e
WHERE CAST(e['msg'] AS STRING) = 'hello'
ORDER BY id, title;
-- Top-level array: expand the VARIANT column itself
SELECT
id,
CAST(e['msg'] AS STRING) AS msg,
CAST(e['title'] AS STRING) AS title
FROM variant_top_array_tbl
LATERAL VIEW explode(data) tmp AS e
WHERE CAST(e['msg'] AS STRING) = 'hello'
ORDER BY id, title;
explodeは各配列要素を行として返し、各要素はVARIANTサブパスを通じて引き続きアクセス可能です。インデックス付き同一要素マッチングにはSEARCH('NESTED(...)')を使用し、さらなる計算やフィルタリングの前に要素を行にフラット化する必要がある場合はLATERAL VIEW explode(...)を使用してください。
互換性と制限
variant_enable_nested_group = trueはvariant_enable_doc_mode = trueと相互排他的です。variant_enable_nested_group = trueはvariant_max_subcolumns_count > 0と相互排他的です。Nested Groupを有効にした後、カラムはスパースカラムプロパティを使用しなくなります。- レガシーの
variant_enable_flatten_nestedスイッチは非推奨であり、variant_enable_nested_groupと同じ機能ではありません。4.1.x新規テーブルにはvariant_enable_nested_groupを使用してください。 - BEコンフィグ
variant_nested_group_max_depthはNested Groupによって追跡される最大ネスト配列深度を制御します。デフォルトは10で、より深いパスはJSONBとして保存されます。 - 同じパスに対してデータ形状を安定させてください。同じパスがスカラーと
array<object>の両方として書き込まれた場合、競合処理がトリガーされ、デフォルトではネスト配列構造が優先されます。
INSERTとロード
INSERT INTO VALUES
CREATE TABLE IF NOT EXISTS variant_tbl (
k BIGINT,
v VARIANT
) PROPERTIES("replication_num" = "1");
INSERT INTO variant_tbl VALUES (1, '{"a" : 123}');
select * from variant_tbl;
+------+-----------+
| k | v |
+------+-----------+
| 1 | {"a":123} |
+------+-----------+
-- v['a'] is a VARIANT
select v['a'] from variant_tbl;
+--------+
| v['a'] |
+--------+
| 123 |
+--------+
-- Accessing a non-existent key returns NULL
select v['a']['no_such_key'] from variant_tbl;;
+-----------------------+
| v['a']['no_such_key'] |
+-----------------------+
| NULL |
+-----------------------+
Load (Stream Load)
# Line-delimited JSON (one JSON record per line)
curl --location-trusted -u root: -T gh_2022-11-07-3.json \
-H "read_json_by_line:true" -H "format:json" \
http://127.0.0.1:8030/api/test_variant/github_events/_stream_load
参照: https://doris.apache.org/docs/dev/data-operate/import/complex-types/variant
読み込み後、SELECT count(*)で検証するか、SELECT * ... LIMIT 1でサンプル確認してください。高スループットの取り込みには、RANDOMバケッティングを使用し、Group Commitを有効にすることをお勧めします。
Output
VARIANTカラムを読み取る際に返されるJSONテキストは、書き込まれたJSONテキストとバイト単位で同一ではありません。JSONオブジェクト内では、入力JSONでの順序に関係なく、キーはソート順(辞書順)で出力されます。
INSERT INTO variant_tbl VALUES
(2, '{ "b": 2, "a": 1, "c": { "y": 20, "x": 10 } }');
SELECT v FROM variant_tbl WHERE k = 2;
+-----------------------------------+
| v |
+-----------------------------------+
| {"a":1,"b":2,"c":{"x":10,"y":20}} |
+-----------------------------------+
ソート処理は全てのレベルで適用されます — トップレベルのキーは a, b, c となり、ネストされたオブジェクトのキーは x, y となります。
サポートされている操作とCASTルール
- VARIANTは他のタイプと直接比較/操作できません。2つのVARIANT間の比較もサポートされていません。
- 比較、フィルタリング、集約、および順序付けについては、サブパスを具象型にCAST(明示的または暗黙的に)してください。
-- Explicit CAST
SELECT CAST(v['arr'] AS ARRAY<TEXT>) FROM tbl;
SELECT * FROM tbl WHERE CAST(v['decimal'] AS DECIMAL(27, 9)) = 1.111111111;
SELECT * FROM tbl WHERE CAST(v['date'] AS DATE) = '2021-01-02';
-- Implicit CAST
SELECT * FROM tbl WHERE v['bool'];
SELECT * FROM tbl WHERE v['str'] MATCH 'Doris';
- VARIANT自体は、ORDER BY、GROUP BY、JOIN KEY、または集約関数の引数として直接使用できません。代わりにサブパスをCASTしてください。
- 文字列はVARIANTに暗黙的に変換できます。
| VARIANT | Castable | Coercible |
|---|---|---|
ARRAY | ✔ | ❌ |
BOOLEAN | ✔ | ✔ |
DATE/DATETIME | ✔ | ✔ |
FLOAT | ✔ | ✔ |
IPV4/IPV6 | ✔ | ✔ |
DECIMAL | ✔ | ✔ |
MAP | ❌ | ❌ |
TIMESTAMP | ✔ | ✔ |
VARCHAR | ✔ | ✔ |
JSON | ✔ | ✔ |
Wide columns
取り込みデータに多数の異なるJSONキーが含まれている場合、Subcolumnizationによって生成されるサブカラムの数が急速に増加する可能性があります。大規模な環境では、これによりメタデータの膨張、書き込み/マージコストの増加、クエリの性能低下が発生する場合があります。「wide columns」(サブカラムが多すぎる)問題に対処するため、VARIANTは2つのメカニズムを提供しています:Sparse columnsとDOC encodingです。
ワークロード選択のガイダンスについては、Variant Workload Guideを参照してください。このセクションでは、メカニズムとその関連プロパティについてのみ説明します。
注意:これら2つのメカニズムは相互排他的です。DOC encodingを有効にするとsparse columnsが無効になり、その逆も同様です。
Sparse columns
仕組み
- システムは非null比率/スパース性によってパスをランク付けします。高頻度(スパース性の低い)パスはSubcolumnizationを通じて独立したサブカラムとして保存され、残りの低頻度(スパース)パスはマージされてsparse columnsに保存されます。抽出されるサブカラムの最大数は
variant_max_subcolumns_countによって制御されます。 - パスがSchema Templateで宣言されている場合、デフォルトではsparse columnsに移動されません。型付きパスをsparse columnsに移動できるようにするには、
variant_enable_typed_paths_to_sparseを設定してください。 - Sparse columnsはシャーディングをサポートします:スパースサブパスを複数のsparse columnsに分散して、カラムごとの読み取りオーバーヘッドを削減し、読み取り効率を向上させます。
variant_sparse_hash_shard_countを使用して、物理的に保存されるsparse columnsの数を指定してください。
参考事項
- ほとんどのキーが類似した非null比率を持つ場合(スパース性のコントラストが少ない)、真にスパースなパスを識別することが困難になり、sparse columnsの利点が減少します。
variant_max_subcolumns_countのデフォルト値は2048で、これはほとんどのワークロードにとって十分です。より多くの抽出済みサブカラムを事前に割り当てるために積極的にこの値を上げることは避けてください。ワークロードで真に大規模なSubcolumnizationが必要な場合は、DOCモードを使用することを推奨します。実用的な上限は10000以下に留めることが推奨されます。variant_sparse_hash_shard_countは「スパースパス数 / 128」として大まかに推定できます。例:JSONキー総数 ≈ 10,000、variant_max_subcolumns_count = 2000の場合、スパースパス ≈ 8000となるため、variant_sparse_hash_shard_countは8000/128付近から開始できます。
DOC encoding(DOCモード)
仕組み
- パスベースクエリのためにパスは引き続きSubcolumnizationを通すことができ、完全なJSONドキュメントを効率的に返すために、元のJSONが追加でstored fieldとして保存されます。
- DOC encodingはシャーディングをサポートします:元のJSONは保存のために複数のカラムに分割され、完全なJSONをクエリする際に再組み立てされます。
variant_doc_hash_shard_countを使用してDOCシャード数を指定してください。 - 小規模バッチ書き込みの場合、Subcolumnizationをスキップして後のマージに先送りできます。これは
variant_doc_materialization_min_rowsによって制御されます。例えば、variant_doc_materialization_min_rows = 10000の場合、10,000行未満の書き込みでは元のJSONのみが保存され、そのバッチでSubcolumnizationはトリガーされません。 - 超wide なワークロードの場合、Subcolumnizationの規模が数万サブカラムに近づく際、DOCモードはより安定した選択肢でもあります。デフォルトのeager Subcolumnizationと比較して、コンパクションメモリは約2/3削減でき、スパースなwide-columnの取り込みスループットは約5-10倍向上します。
VARIANTカラムが非常にwideで、クエリが全体のドキュメントを読み取ることが多い場合、DOCモードは多数のサブカラムからドキュメントを再構成する場合と比較して、SELECT variant_colの性能を桁違いに向上させることができます。
参考事項
- DOCモードは
variant_enable_doc_mode = trueが必要です。 - DOCモードでは、Schema Templateを介して宣言される型付きパスは数値、文字列、配列型に制限されます。
variant_doc_hash_shard_countは「JSONキー総数 / 128」として大まかに推定できます。
完全なプロパティリストについては、以下の「Configuration」セクションを参照してください。
制限事項
- Wide tablesの最適化:
VARIANT型によって生成される動的サブカラムが多数ある(例:2000カラム以上)wide tablesの場合、テーブルPROPERTIESで"storage_format" = "V3"を指定してStorage Format V3を有効にすることを強く推奨します。これにより、カラムメタデータがSegment Footerから分離され、ファイルのオープンが高速化され、メモリオーバーヘッドが削減されます。 variant_enable_nested_group = trueはvariant_enable_doc_mode = trueおよびvariant_max_subcolumns_count > 0と相互排他的です。テーブル作成時にのみ指定できます。NESTED()内のフィールドはネストされたパスに相対的である必要があり、ネストしたNESTED()呼び出しはサポートされていません。- JSONキー長 ≤ 255。
- プライマリキーまたはソートキーにはできません。
- 他の型内にネストできません(例:
Array<Variant>、Struct<Variant>)。 - DOCモード以外では、VARIANT カラム全体を読み取ると全てのサブパスがスキャンされます。非常にwideなカラムの場合、DOCモードが有効でない限り、直接的な
SELECT variant_colは一般的に推奨されません。カラムに多数のサブパスがある場合は、LIKEなどのオブジェクト全体検索のために追加のSTRING/JSONBカラムに元のJSON文字列を保存することを検討してください:
CREATE TABLE example_table (
id INT,
data_variant VARIANT
);
SELECT * FROM example_table WHERE data_variant LIKE '%doris%';
-- Better: keep the original JSON string for whole-object matching
CREATE TABLE example_table (
id INT,
data_string STRING,
data_variant VARIANT
);
SELECT * FROM example_table WHERE data_string LIKE '%doris%';
設定
3.1以降、VARIANTは列に対する型レベルプロパティをサポートしています:
CREATE TABLE example_table (
id INT,
data_variant VARIANT<
'path_1' : INT,
'path_2' : STRING,
properties(
'variant_max_subcolumns_count' = '2048',
'variant_enable_typed_paths_to_sparse' = 'true',
'variant_sparse_hash_shard_count' = '64'
)
>
);
| プロパティ | 説明 |
| `variant_max_subcolumns_count` | Subcolumnizationを通過できるパスの最大数。この閾値を超えると、新しいパスは共有データ構造に格納される場合があります。デフォルト: 2048(推奨)、これはほとんどのワークロードに対して既に十分です。値を大きく設定しすぎないでください。ワークロードが本当に非常に大規模な抽出サブカラムスケールを必要とする場合は、DOC modeを使用することをお勧めします。0は制限なしを意味します;10000を超えないでください。 |
| `variant_enable_typed_paths_to_sparse` | デフォルトでは、型付きパスは常にSubcolumnizationに参加します(`variant_max_subcolumns_count`にはカウントされません)。`true`に設定すると、型付きパスも閾値にカウントされ、共有構造に移動される場合があります。 |
| `variant_sparse_hash_shard_count` | スパースカラムのシャード数。読み取りパフォーマンスを向上させるため、スパースサブパスを複数のスパースカラムに分散します。デフォルト: 1;スパースサブパスの数に基づいて調整してください。 |
CREATE TABLE example_table (
id INT,
data_variant VARIANT<
'path_1' : INT,
'path_2' : STRING,
properties(
'variant_enable_doc_mode' = 'true',
'variant_doc_materialization_min_rows' = '10000',
'variant_doc_hash_shard_count' = '64'
)
>
);
| プロパティ | 説明 |
| `variant_enable_doc_mode` | DOCエンコーディングモードを有効にします。`true`の場合、元のJSONがstored fieldとして保存され、JSON文書全体を素早く返すことができます。DOCモードはsparse columnsと相互排他的です。デフォルト: `false`。 |
| `variant_doc_materialization_min_rows` | DOCモードでSubcolumnizationをトリガーする最小行閾値。行数がこの値を下回る場合は元のJSONのみが保存され、compactionによってファイルがマージされて閾値に達した後にSubcolumnizationが実行されます。小規模バッチ書き込みのオーバーヘッドを削減するのに役立ちます。 |
| `variant_doc_hash_shard_count` | DOCエンコーディングのシャード数。元のJSONが指定された数のカラムに分割されて保存され、JSON全体をクエリする際に再構成されます。デフォルト: 64; JSONサイズと同時実行性に基づいて調整してください。 |
CREATE TABLE example_nested_group_table (
id INT,
data_variant VARIANT<
properties(
'variant_enable_nested_group' = 'true'
)
>
);
| Nested Groupプロパティ | 説明 |
| `variant_enable_nested_group` | Variant Nested Groupを有効にします。`true`に設定すると、トップレベルのオブジェクト配列を含む`VARIANT`内の`array<object>`値がネストされた要素ごとに格納され、同一要素マッチングのために`SEARCH('NESTED(...)')`でクエリできます。デフォルトはセッション変数`default_variant_enable_nested_group`に従い、現在のデフォルトは`false`です。このプロパティはテーブル作成時にのみ指定できます。 |
| `default_variant_enable_nested_group` | カラムプロパティが指定されていない場合に、新しく作成される`VARIANT`カラムでNested Groupをデフォルトで有効にするかどうかを制御するセッション変数。 |
| `variant_nested_group_max_depth` | Nested Groupによって追跡されるネスト配列の最大深度を制御するBE設定。デフォルトは10です。 |
制限時の動作とチューニング提案:
- しきい値を超えた後、新しいパスは共有構造に書き込まれます。Rowsetマージによって、一部のパスが共有構造にリサイクルされる場合もあります。
- システムは、null以外の比率が高く、アクセス頻度の高いパスをSubcolumnizationで保持することを優先します。
- Subcolumnizationで10,000パス近くになると強力なハードウェア(ノードあたり≥128G RAM、≥32C推奨)が必要です。ワークロードが既にこの範囲に近い場合は、まずDOCモードの評価を優先してください。
- 取り込みチューニング:クライアント
batch_sizeを適切に増加させるか、Group Commit(必要に応じてgroup_commit_interval_ms/group_commit_data_bytesを増加)を使用してください。 - パーティションプルーニングが不要な場合は、RANDOMバケッティングを検討し、single-tabletローディングを有効にしてcompaction書き込み増幅を削減してください。
- BEチューニングノブ:
max_cumu_compaction_threads(≥8)、vertical_compaction_num_columns_per_group=500(vertical compactionを改善するがメモリを増加)、segment_cache_memory_percentage=20(メタデータキャッシュ効率を改善)。 - Compaction Scoreを監視してください。継続的に上昇している場合、compactionが遅れているため、取り込み圧力を軽減してください。
- VARIANTで大きな
SELECT *を避け、SELECT v['path']のような具体的な射影を優先してください。
注意:Stream Loadエラー[DATA_QUALITY_ERROR]Reached max column size limit 2048(2.1.xおよび3.0.xのみ)が表示される場合、マージされたタブレットスキーマがカラム制限に達したことを意味します。variant_max_merged_tablet_schema_sizeを増加させることができます(4096を超える設定は推奨されません。強力なハードウェアが必要です)。
カラム数と型の検査
アプローチ1:variant_typeを使用して行ごとのスキーマを検査(より精密、コストが高い):
SELECT variant_type(v) FROM variant_tbl;
アプローチ2: Subcolumnizationによって抽出されたサブパスを表示するためにDESCを拡張:
SET describe_extend_variant_column = true;
DESC variant_tbl;
DESCRIBE ${table_name} PARTITION ($partition_name);
両方を使用: アプローチ1は正確、アプローチ2は効率的。
JSON型との比較
- ストレージ: JSONはJSONB(行指向)として保存される。VARIANTは書き込み時にSubcolumnizationを使用(高圧縮、小サイズ)。
- クエリ: JSONは解析が必要。VARIANTは列を直接スキャンし、通常はるかに高速。
ClickBench(43クエリ):
- ストレージ: VARIANTはJSONと比較して約65%節約。
- クエリ: VARIANTはJSONより8倍以上高速で、事前定義された静的列に近い。
ストレージ容量
| 型 | サイズ |
|---|---|
| 事前定義列 | 12.618 GB |
| VARIANT | 12.718 GB |
| JSON | 35.711 GB |
約65%の容量節約
| 実行 | 事前定義 | VARIANT | JSON |
|---|---|---|---|
| 1回目(コールド) | 233.79s | 248.66s | ほとんどタイムアウト |
| 2回目(ホット) | 86.02s | 94.82s | 789.24s |
| 3回目(ホット) | 83.03s | 92.29s | 743.69s |
FAQ
- VARIANTの
nullとSQLのNULLは異なりますか?- いいえ。同等です。
- なぜクエリ/インデックスが動作しないのですか?
- パスを正しい型にCASTしているか、競合により型がJSONBに昇格されたか、またはサブパスではなくVARIANT全体にインデックスを期待していないかを確認してください。
- VARIANT列に書き込まれる際にDECIMALが精度を失うのはなぜですか?
- VARIANT列への書き込み時、サブカラム型はDECIMALとして推論されません。数値はDOUBLEとして保存され、末尾の小数点以下が削除される可能性があります。Schema Template経由でサブパスをDECIMALとして宣言しても(例:
pm25 VARIANT<'xxx': DECIMAL(6, 2)>)、値は最初にDOUBLEとして解析され、書き込みパスでDECIMALに変換されるため、精度は完全には保証されません。JSON値が文字列として書き込まれ(例:'{"num": "12.345"}')、対応するSchema Template DECIMAL宣言(例:DECIMAL(9, 3))と組み合わせれば、文字列は書き込み時に直接DECIMALに解析され、精度が保持されます。
- VARIANT列への書き込み時、サブカラム型はDECIMALとして推論されません。数値はDOUBLEとして保存され、末尾の小数点以下が削除される可能性があります。Schema Template経由でサブパスをDECIMALとして宣言しても(例:
- Variant Nested Groupと旧
variant_enable_flatten_nestedスイッチの違いは何ですか?variant_enable_flatten_nestedはレガシーの実験的スイッチであり、4.1.xの新テーブルには推奨されません。variant_enable_nested_groupは、ネストされたオブジェクトの配列に対する新しいストレージと検索機能で、SEARCH('NESTED(...)')による同一要素述語マッチングをサポートします。