博客 / 詳情

返回

查詢性能提升 10 倍、存儲空間節省 65%,Apache Doris 半結構化數據分析方案及典型場景

隨着業務的蓬勃發展,各企業對數據處理的靈活性和可擴展性提出了更高的要求。在此背景下,JSON、XML 等半結構化數據憑藉其較強的靈活性在眾多企業得到廣泛應用。然而,傳統關係型數據庫結構嚴格,難以應對半結構化數據的複雜性及多樣性。為有效存儲及分析這些數據,Apache Doris 針對不同應用場景提供了 Array、Map、Struct、JSON、VARIANT半結構化數據存儲分析解決方案。

本文我們將聚焦企業最普遍使用的 JSON 數據,分別介紹業界傳統方案以及 Apache Doris 半結構化數據存儲分析的三種方案,並通過圖表直觀展示這些方案的優勢與不足。同時,結合具體應用場景,分享不同需求場景下的使用方式,幫助用户快速選擇最合適的 JSON 數據存儲及分析方案。

半結構化數據特點及挑戰

業界通常將數據分為結構化數據、非結構化數據、半結構化數據這三大類型:

  • 結構化數據:關係型數據庫是一種典型的結構化數據存儲方式,其核心特點是結構嚴格且固定。例如,一個包含五列數據的表,其數據類型可能是字符串(string)、整數(int)或日期(date)等。字段名和類型均是預先設定、不可輕易改變,具備讀寫性能出色的優勢。
  • 非結構化數據:非結構化數據指沒有固定結構的數據,例如文本、音頻和視頻等,這類數據缺乏明顯的結構特徵。例如,進行文本檢索時,需要查找特定的關鍵字或短語。_(Apache Doris 從 2.0 版本開始,提供了倒排索引等功能,可以實現對非結構化文本數據的高效檢索,包括關鍵詞檢索、短語檢索等。)_
  • 半結構化數據: 半結構化數據雖然擁有一定的結構,但不嚴格固定,具有很強的靈活性。比較典型的是 JSON 格式,可以便捷地增加新字段或刪除不需要的字段,以適應數據交互和存儲的需求。

Github 用户操作記錄日誌 GH Archive 是典型的半結構化 JSON 數據,通過下方示例 CreateEvent 和 PushEvent ,展示真實的數據。

  • CreateEvent

    {
      "id": "37066529202",
      "type": "CreateEvent",
      "actor": {
        "id": 151583193,
        "login": "BlankTMing",
        "display_login": "BlankTMing",
        "gravatar_id": "",
        "url": "https://api.github.com/users/BlankTMing",
        "avatar_url": "https://avatars.githubusercontent.com/u/151583193?"
      },
      "repo": {
        "id": 780596894,
        "name": "BlankTMing/ManifestAutoUpdate-",
        "url": "https://api.github.com/repos/BlankTMing/ManifestAutoUpdate-"
      },
      "payload": {
        "ref": "2715611_469785097560218038",
        "ref_type": "tag",
        "master_branch": "main",
        "description": null,
        "pusher_type": "user"
      },
      "public": true,
      "created_at": "2024-04-01T23:00:00Z"
    }
    
  • PushEvent

    {
      "id": "37066529220",
      "type": "PushEvent",
      "actor": {
        "id": 73488070,
        "login": "hafsa1319",
        "display_login": "hafsa1319",
        "gravatar_id": "",
        "url": "https://api.github.com/users/hafsa1319",
        "avatar_url": "https://avatars.githubusercontent.com/u/73488070?"
      },
      "repo": {
        "id": 746560097,
        "name": "hafsa1319/akademi_report",
        "url": "https://api.github.com/repos/hafsa1319/akademi_report"
      },
      "payload": {
        "repository_id": 746560097,
        "push_id": 17799451996,
        "size": 1,
        "distinct_size": 1,
        "ref": "refs/heads/main",
        "head": "fc7a15d71539a3588f43e41f9034bfb4b4464358",
        "before": "e29be382e67485ff5a8a88264f9b3272b2366c3a",
        "commits": [
          {
            "sha": "fc7a15d71539a3588f43e41f9034bfb4b4464358",
            "author": {
              "email": "73488070+hafsa1319@users.noreply.github.com",
              "name": "hafsa1319"
            },
            "message": "Add or update home/hafsa-report/htdocs/report.hafsa.de/akademi/csv/telcHS2402.csv",
            "distinct": true,
            "url": "https://api.github.com/repos/hafsa1319/akademi_report/commits/fc7a15d71539a3588f43e41f9034bfb4b4464358"
          }
        ]
      },
      "public": true,
      "created_at": "2024-04-01T23:00:00Z"
    }
    

參考 Wikipedia 上的定義,結合實際業務落地的經驗,半結構化數據具有以下特點:

semi-structured-definition.png

  • 不嚴格遵循結構化表模型:半結構化數據不嚴格遵循關係數據庫中的表格結構,通常包含標籤(tags)或其他形式的標記,以表明其語義或字段名。以上方 GH Archive 示例,"id", "type", "payload" 是標籤或者字段名。
  • 自描述結構但不固定: 半結構化數據具有一定自描述性,一般通過鍵值對(Key-Value Pairs)描述內部結構。這種結構並不固定,可能包含不同數量的字段或類型。以上方 Github Event 示例,PushEvent 的 payload 字段就比 CreateEvent 多了ref head commits 等字段。
  • 通常有嵌套結構: 嵌套結構的複雜性較高,表現為一個結構體內部嵌套另一個結構體,甚至結構體或數組中再嵌套其他結構體或數組,形成多層次、複雜的數據結構。以上方 GH Archive 示例,CreateEvent 中 actor repo payload有簡單的嵌套子字段,而 PushEvent 的 payload 中commits 字段則出現了數組嵌套結構體、結構體再嵌套結構體的複雜結構。

上述特點為半結構化數據的存儲和分析帶來很大的挑戰,也是業界數據庫要解決的主要問題:

  • 如何支持靈活的 Schema:半結構化數據具備較高的靈活性,字段隨着業務發展而增加/減少,類型也可能變化,數據中的嵌套結構也讓字段變的更加複雜,因此要求數據庫能夠支持靈活的 Schema。
  • 如何高效存儲:半結構化數據中包含大量重複的自描述內容,比如大量重複的字段名,通常是由機器產生。如果按原始數據存儲,數據冗餘存儲帶來的資源浪費非常高,因此要求數據庫能夠高效存儲。
  • 如何極速分析:半結構化數據通常為文本形式,直接對文本解析和分析雖然可行但性能較差。特別是在分組、聚合、過濾等操作時,要從大量的字段中分析其中的幾個字段,將帶來很多不必要的 IO 和解析開銷。

接下來,我們就以 JSON 數據為例,瞭解業界為應對這些挑戰的常見解決方案。

傳統解決方案

01 通過 ETL 轉為結構化數據

方案一是在 ETL 過程將半結構化數據轉化為結構化數據,主要藉助 ETL 工具 / 數據庫導入過程中實現。比如在 Doris 中,可以藉助導入的 JSON 字段映射功能,將數據映射到預設的表結構中。

該方案的優勢是:轉化為結構化形式後,可充分利用結構化數據處理的優勢,提供較高的存儲壓縮率和出色的分析性能。

該方案的問題是:當上遊數據源字段發生變化(如增加或刪除字段)時,下游表結構也進行相應修改。如不修改表結構,新增的數據將無法完整寫入。而修改過程非常繁瑣,通常需要多個團隊協作與配合,處理起來並不高效,且這種方式也喪失了半結構化數據的靈活性。

02 String 存儲和 JSON 函數分析

方案二是將 JSON 數據轉存到 String 字段中,String 支持存儲任意文本數據,可解決 Schema 靈活性差的問題。當需要對這些 JSON 數據查詢分析時,可使用專門的 JSON 函數提取所需字段,如可通過json_extractjson_extract_intjson_extract_double等函數解析並提取特定字段值。

該方案的問題是:每次查詢都需要使用 JSON 函數解析和遍歷整行 JSON 文本,效率低、分析性能差。此外,由於 JSON 文本以行為單位進行存儲,其壓縮效率不如列式存儲高。

03 Elasticsearch Dynamic Mapping

方案三為 Elasticsearch 的 Dynamic Mapping ,該方案可自動識別新增 JSON 數據的字段名和類型,並將字段動態添加到 Elasticsearch Index Mapping (類似 Table Schema) 中。

該方案的問題是:

  • 字段類型一旦確定不可更改,若字段首次被寫入為整型(int),後續則必須保持為整型;如果嘗試寫入非整型數據(如浮點型 float 或者字符串類型 string),Elasticsearch 將拒絕寫入並可能丟棄這條數據,限制了數據類型隨業務發展而演變的靈活性。
  • 當寫入數據包含大量字段時,Elasticsearch Mapping 會迅速膨脹,這是因為 Elasticsearch 會將每個字段展開,字段多的時候(比如超過 500)元數據壓力增大,嚴重影響查詢,而且 Mapping 只增不減,即便刪除字段多的行也不能減少元數據。

基於 Apache Doris 的半結構化數據存儲及分析方案

針對傳統方案存在的問題,Apache Doris 結合不同場景下對半結構化數據存儲和分析的需求,提供了三種解決方案,用户可以根據實際場景靈活選擇。

01 Array Map Struct

Array、 Map 、Struct 數據類型支持嵌套的固定 Schema,常用於用户行為和畫像分析、查詢數據湖中 Parquet ORC 等格式數據的場景。

Array Map Struct 可以存儲複雜結構數據,Array 存儲相同類型的數組,Map 存儲鍵值對(Key-Value ),Struct 存儲 n 元組,它們之間可以相互嵌套。

  • 優勢:採用列式存儲,可實現較高的壓縮率,節省大量存儲空間;因嵌套結構的字段和類型是預先定義且相對固定的,在寫入和查詢時不再需要動態推斷數據的 Schema,執行效率較高。
  • 不足:雖可以預先定義出複雜的嵌套結構,但是一旦定義後結構不能隨着數據變化自適應。

02 JSON

JSON 數據類型支持嵌套的不固定 Schema,常用於點查和部分分析場景。

JSON 數據類型是二進制存儲類型,具備 JSON String 的靈活性,任意合法的 JSON 數據均可進行存儲,分析時通過 JSON 函數來提取對應字段。

  • 優勢:點查性能好,JSON 採用行存形式進存儲,且 JSON 在寫入過程中已完成 JSON 的解析,可從二進制中直接讀取數據,查詢效率至少比 JSON String 快 2 倍。
  • 不足:JSON 存儲壓縮率低於列存,存儲成本也相對較高。同時,因在查詢時需要先讀取整行 JSON 二進制數據、再讀取需要分析的字段,讀取效率不如行存高效。

03 VARIANT

VARIANT 數據類型支持嵌套的不固定 Schema,常用於 Log、 Trace、 IoT 等分析場景,業界類似的解決方案還有前文所述的 Elasticsearch Dynamic Mapping。

VARIANT 數據類型可以存儲任何合法的 JSON,可自動從 JSON 中抽取字段並推斷其類型,並將這些字段存儲為 VARIANT 列的子列。這種列式存儲方式使得 VARIANT 具備很好的分析性能,當進行聚合/過濾/排序等查詢時,只需要讀取 Variant 子列數據即可,不會產生額外的數據解析開銷,查詢性能可獲得數量級的提升。

相比於 Elasticsearch Dynamic Mapping ,Doris VARIANT 的優勢在於:

  • 允許寫入不同字段類型,數據文件內部使用最小公共類型存儲,數據文件之間採用不同類型存儲,互不影響。查詢時,可以使用最小公共類型或者用户指定的類型查詢。
  • 可以將出現頻次較低的字段合併為二進制 JSON 存儲,以此避免字段過多引發子列和文件膨脹的問題,可以兼顧性能和數據結構的靈活性。

在基於 ClickBench 的測試數據集上,VARIANT 有很好的性能表現。

VARIANT.png

  • 在存儲方面,其性能與 ETL 轉結構化方案相當,均有較低的存儲佔用;相較於 JSON String,存儲資源節省達65%
  • 在查詢方面,其性能與 ETL 轉結構化方案相當,冷熱查詢性能差異在 10% 以內;相較於 JSON String 來説,冷查詢有 10 倍以上提升、熱查詢有 8.4 倍的提升,在用户實際的應用場景中,也驗證了相似的結果。

方案對比

為直觀比較各方案,我們通過圖表來展示 ETL 轉結構化、JSON String/Binary、Elasticsearch Dynamic Mapping 、Array Map Struct、JSON、VARIANT 等方案的特點,從 Schema 的靈活性、存儲效率和分析性能等維度評估各方案的優勢和侷限性。 (橫座標軸為 Schema 靈活性、縱座標軸為存儲效率 & 分析性能

方案對比.jpeg

由上圖可得出結論:

  • ETL 轉結構化方案的的分析性能表現最佳,但 Schema 靈活性最差。
  • JSON String / Binary 的 Schema 靈活性最佳,但是其分析性能均比較低。
  • Doris VARIANT 和 Dynamic Mapping 在靈活性和性能方面表現均比較好,但整體而言 Doris VARIANT 更優,不僅是存儲和分析性能強於 Dynamic Mapping,還體現在 VARIANT 能夠很好解決字段類型固定和字段個數膨脹的痛點問題。

典型應用場景

接下來,我們將從用户常見的典型應用場景入手,介紹在不同場景中應選擇哪種解決方案,以獲得最佳的使用體驗和性能表現。

01 用户畫像與行為分析場景

在用户畫像與行為場景中,有時會遇到包含多個值的複雜標籤,比如 “喜歡的顏色”,可將顏色編號成整數,然後用 ARRAY<INT> 存儲該標籤。

CREATE TABLE `customer_profile` (
  `rid` bigint NOT NULL,
  `pid` bigint NOT NULL,
  `last_update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
  `tag_cdp_1` int NULL,
  `tag_cdp_2` varchar,
  `tag_cdp_3` varchar,
  `tag_cdp_4` int NULL,
  `tag_cdp_5` varchar,
  `tag_1` text NULL,
  `tag_2` array<int> NULL,
  `tag_3` int NULL,
  `tag_4` text NULL,
  `tag_5` array<int> NULL,
  `tag_6` int NULL,
  `tag_7` array<int> NULL
) ENGINE=OLAP
UNIQUE KEY(`rid`, `pid`)
DISTRIBUTED BY HASH(`rid`, `pid`) BUCKETS 30

查詢時可以篩選多值標籤是否包含某些值,比如使用 array_overlap 函數檢查 tag_5 裏面是否有 3, 6 或者 8。

SELECT * FROM customer_profile
WHERE pid = 1001
  AND array_overlap(tag_5, [3, 6, 8])

02 數據湖查詢加速場景

在數據湖查詢加速場景中,在對接 Hive、Iceberg、Hudi 等外部數據源時,經常出現 ARRAY MAP STRUCT 等複雜嵌套數據類型,我們可以將這些數據類型直接映射到 Doris 內置的 ARRAY MAP STRUCT 類型。

如下示例,在 Doris 中創建 Hive CATALOG 並切換,可以快速讀取 Hive 中的表。還可通過 DESC 查看錶 st 結構中所包含的複雜嵌套類型,包括 ARRAY<FLOAT>類型 的 usage 字段、MAP<STRING, FLOAT>類型的 signal字段、MAP 嵌套 ARRAYext 擴展字段。

CREATE CATALOG hive PROPERTIES (
    'type'='hms',
    'hive.metastore.uris' = 'thrift://172.0.0.1:9083',
    'hadoop.username' = 'hive'
);

SWITCH hive;

DESC st;
+--------+---------------------------+------+-------+---------+-------+
| Field  | Type                      | Null | Key   | Default | Extra |
+--------+---------------------------+------+-------+---------+-------+
| id     | VARCHAR(255)              | Yes  | true  | NULL    |       |
| status | TINYINT                   | Yes  | false | NULL    |       |
| start  | DATETIME                  | Yes  | false | NULL    |       |
| end    | DATETIME                  | Yes  | false | NULL    |       |
| usage  | ARRAY<FLOAT>              | Yes  | false | NULL    |       |
| signal | MAP<STRING, FLOAT>        | Yes  | false | NULL    |       |
| ext    | MAP<STRING, ARRAY<INT>>   | Yes  | false | NULL    |       |
+--------+---------------------------+------+------+---------+-------+

下面的查詢首先篩選出 2013-11-04 的數據,然後從ext這個 MAP 字段中,篩選出 Key 為'tags' 的 ARRAY 中包含55的記錄,最後按status字段進行分組,使用array_avg函數計算usage這個 ARRAY 類型字段的平均值。

SELECT status, avg(array_avg(usage)) as avg_usage FROM st
WHERE array_contains(ext['tags'], 55)
  AND start >= '2013-11-04 00:00:00'
  AND   end <= '2013-11-04 23:59:59'
GROUP BY status;

03 Log 場景

在日誌存儲和分析的場景中,日誌數據通常包含一些靜態字段(時間戳、機器名稱、文件路徑)、日誌文本消息以及擴展字段。擴展字段用於存儲不固定的信息或屬性,例如某個日誌中可能包含一個名為ext的擴展字段,用於記錄與日誌相關的特定信息或數據。如下所示:

{
    "time": 2024-04-09 16:06:03.684886
    "source": "fluentd_tcp"
    "host": "host1"
    "status": "info"
    "filepath": "path1"
    "container_id": "xxx"
    "container_name": "xxx"
    "message": "....."
    "ext": {
        "nameSpace": "kube-public",
        "level": "info",
        "service": "console-media",
        "category": "debug",
        "tag": "[]",
        "serviceID": "console-media-6b57fc7758-nplck",
        "date_ns": 886517,
        "index": "default",
        "cluster": "UcloudKubernetes"
    }
}

包含擴展字段的日誌數據結構使得數據擴展更加便捷、靈活和多樣化,但也對數據的存儲和分析技術提出了更高的要求。而使用 VARIANT 可以很好的解決這一問題。

在建表時,如果擴展字段ext使用 VARIANT 類型,可使數據靈活寫入。

CREATE TABLE log (
    `time` datetime(6) NULL,
    `source` text NULL,
    `host` text NULL,
    `status` text NULL,
    `filepath` text NULL,
    `container_id` text NULL,
    `container_name` text NULL,
    `message` text NULL,
    `ext` variant NULL,
    INDEX idx1 (`message`) USING INVERTED PROPERTIES("parser" = "chinese")
)

在查詢時,可以使用特定的語法訪問 VARIANT 的子列。如下所示的查詢中,主要按照時間和服務名稱來統計錯誤數量。

  • 首先,在WHERE條件中將時間戳轉換為小時單位,並提取出EXT字段中的service字段值。
  • 接着,計算滿足條件的記錄數量(count)並進行聚合。如需提取namespace字段時(variant類型的子字段),無需讀取整個 VARIANT 字段的內容,而只需訪問 VARIANT 擴展的特定子列即可,這種數據訪問方式使得查詢性能更高。
  • 最後,按照小時和服務名稱對結果進行分組,並按時間進行排序,就可以得到一個錯誤趨勢圖。該趨勢圖可幫助用户直觀地瞭解系統在不同時間段內的錯誤發生情況,為故障排查和性能優化提供有力支持。
SELECT
hour_floor(`time`) as hour,
cast (ext['service'] as text) as service,
count() as cnt
FROM log WHERE time >= t1 AND time < t2
AND (`status` = "error") AND (`source` = "abc")
AND ext['nameSpace'] = "bcd"
GROUP BY hour, service
ORDER BY hour LIMIT 100;

在日誌場景使用 VARIANT 的優勢在於:

  • 允許字段類型變化,不會因為數據變化而出現寫入異常。
  • 可自動處理擴展字段增減,過期刪除的數據字段不會殘留。
  • 相比 String 或 JSON ,其壓縮率更高、分析性能也更強。

04 Trace 場景

Trace 場景通常應用於可觀測領域,特別是在容器化微服務環境中。在這種環境下,服務之間的調用可能會產生大量的跟蹤數據。例如,一個請求可能經過多個服務,每個服務都會生成一條 Trace 數據,這些 Trace 數據對於問題診斷和性能分析至關重要。

Trace 通常包含attrs屬性字段,這些字段均是可擴展的。由於不同的模塊和服務輸出的格式可能有所不同,因此無法要求其具備固定的 Schema。在其他數據庫中,通常會使用 Map 來存儲 attrs字段,Map 的 Key 的個數是可以擴展的,但在查詢某個 Key 對應的 Value 時,需要遍歷所有 Key Value 對,性能較低,而這個操作在 Trace 場景中比較常見。

{
    "time: "2024-04-09 16:13:51.381141",
    "message": "...",
    "__namespace": "tracing",
    "source": "opentelemetry",
    "service": "iov-fleet-mqtt",
    "operation": "fleet/ListFleets",
    "status: "ok",
    "parent_id": "0",
    "resource": "fleet/ListFleets",
    "span_id": "xxxx",
    "trace_id": "xxxx",
    "duration": 3810,
    "start": 1712650431181241,
    "attrs": {
        "rpc_system": "grpc",
        "rpc_method": "ListFleets",
        "service_sub": "iov-fleet-mqtt",
        "k8s_cluster": "sh-test-uk8s",
        "date_ns": 141210,
        "rpc_service": "fleet.FleetService",
        "service_name": "iov-fleet-mqtt",
        "rpc_grpc_status_code": 0
    }
}

為了更好的滿足這種需求,可以使用 Doris 的 VARIANT 列類型來存儲 attrs 字段。當進行查詢分析時,與 Log 場景類似,可以根據特定條件高效地篩選和提取這些屬性字段,避免遍歷整個 Map 的性能開銷。

CREATE TABLE trace (
    `time` datetime(6) NULL,
    `message` text NULL,
    `source` text NULL,
    `service` text NULL,
    `endpoint` text NULL,
    `operation` text NULL,
    `status` text NULL,
    `parent_id` text NULL,
    `resource` text NULL,
    `span_id` text NULL,
    `trace_id` text NULL,
    `duration` bigint(20) NULL,
    `start` bigint(20) NULL,
    `attrs` variant NULL
)

例如,如果查詢中涉及到'e`rror_stack' `IS NOT NULL的條件,使用 VARIANT 列的查詢效率比較高,原因是無需讀取整個attribute的 JSON 結構,只需讀取與errorStack相關的部分。此外,通過利用索引(如 ZoneMap 索引或倒排索引),可以進一步加速此類查詢。

SELECT
`time`, `message`, `source`, `service`, `endpoint`, `operation`, `status`, `parent_id`, `resource`, `span_id`, `trace_id`, `duration`, `start`, cast(`attrs` as text) as `attrs`
FROM trace
WHERE time >= t1 AND time < t2
AND `service` = "pms-java-gate"
AND attrs['error_stack'] IS NOT NULL
ORDER BY `time` DESC LIMIT 50

在 Trace 場景下,VARIANT 有下面一些優勢:

  • 按 Trace 擴展字段中的某一個進行篩選,效率比 JSON 和 MAP 更高。
  • 相比 JSON 或 MAP,其存儲壓縮率更高。
  • Trace 擴展字段可以隨意增減。

05 IoT 車聯網

IoT 車聯網場景中,有許多設備或車輛具備標籤字段(如 tags),這些字段可能包含數值和數組。

{
    "date": 20240114,
    "plate_name": "xxx",
    "base_dir": "xxx",
    "pack_dir": "xxx",
    "naive_gt_dir": "xxx",
    "gt_dir": "xxx",
    "sync_dir": "xxx",
    "sync_index": [1755],
    "lidar_dir": "xxx",
    "calib_dir": "xxx",
    "weather": NULL,
    "scene": NULL,
    "tags": {
        "a": 2,
        "b": [0,1,0,0,1],
        "c": [0,0,0,0,0],
        "d": 0
    }
}

建表時,tags 字段使用 VARIANT 類型。

CREATE TABLE cars (
    `time` datetime(6),
    `name` TEXT,
    `base_dir` TEXT,
    `pack_dir` TEXT,
    `naive_gt_dir` TEXT,
    `gt_dir` TEXT,
    `sync_dir` TEXT,
    `lidar_dir` TEXT,
    `calib_dir` TEXT,
    `weather` TEXT,
    `scene` TEXT,
    `tags` VARIANT
)

查詢時,對於 tags 裏面的數值字段 a 可以用普通的比較條件,對於 tags 中的數組字段 b,可以使用 array_contains 來檢查是否包含特定值。

SELECT * 
FROM cars
WHERE 
time >= t1 AND time < t2
AND tags['a'] > 10
AND array_contains(tags['b'], 1)
ORDER BY time DESC LIMIT 100

在 IoT 和車聯網場景下,VARIANT 的優勢主要包含以下幾點:

  • 相對於時序數據庫或物聯網數據庫 OLAP 分析功能更豐富,列式存儲分析性能更好。
  • 支持上千稀疏列,在實際應用中,許多設備的標籤字段不一樣,可能只有部分設備包含特定標籤。VARIANT 利用稀疏列的特性,避免了將稀疏字段拆分為多個獨立列,從而提高了存儲效率。此外,由於數據在一段時間內往往具有局部性,即某段時間內上報的數據在標籤上可能較為密集,但整體來看則相對稀疏。
  • 有更高的壓縮率,特別是相較於 String 和 JSON 類型。

結束語

以上就是 Apache Doris 關於半結構化數據 JSON 的解決方案,不論是 Array Map Struct 、JSON 還是 VARIANT 方案,均沒有絕對的優劣之分,可通過實際的應用場景,選擇最合適的解決方案。

  • Array Map Struct:支持嵌套的固定 Schema,常用於用户行為和畫像分析、查詢數據湖中 Parquet ORC 等格式數據的場景。
  • JSON:支持嵌套的不固定 Schema,常用於點查和部分分析場景。點查性能好,查詢效率至少是 JSON String 的 2 倍。
  • VARIANT :支持嵌套的不固定 Schema,常用於 Log、 Trace、 IoT 等分析場景。相較於 JSON String ,存儲空間節省達 64%,冷查詢 10 倍以上提升、熱查詢有 8.4 倍的提升。
user avatar
0 位用戶收藏了這個故事!

發佈 評論

Some HTML is okay.