Databend에서 JSON 설계 및 쿼리

9466 단어
JSON(JavaScript Object Notation)은 일반적으로 사용되는 반구조화된 데이터 유형입니다. 자체 설명 스키마 구조를 통해 JSON은 Array, Object 등과 같은 다단계 중첩 데이터 유형을 포함하여 모든 데이터 유형을 보유할 수 있습니다. 표 형식 데이터 구조의 필드를 따릅니다.

최근 몇 년 동안 데이터 볼륨이 급격히 증가함에 따라 많은 플랫폼에서 반구조화된 데이터 유형(예: JSON)을 사용하고 최대한 활용하기 시작했습니다. 예를 들어 개방형 인터페이스를 통해 다양한 플랫폼에서 공유되는 JSON 데이터와 JSON 형식으로 저장된 공용 데이터 세트 및 애플리케이션 로그가 있습니다.

Databend는 구조화된 데이터 유형과 JSON을 지원합니다. 이 게시물은 Databend의 JSON 데이터 유형에 대해 자세히 설명합니다.



Databend에서 JSON 작업



Databend는 반구조화된 데이터를 VARIANT(JSON이라고도 함) 데이터 유형으로 저장합니다.

CREATE TABLE test 
  ( 
     id INT32, 
     v1 VARIANT, 
     v2 JSON 
  );


JSON 데이터는 "parse_json"또는 "try_parse_json"함수를 호출하여 생성해야 합니다. 입력 문자열은 Null, Boolean, Number, String, Array 및 Object를 포함하는 표준 JSON 형식이어야 합니다. 유효하지 않은 문자열로 인해 구문 분석에 실패한 경우 "parse_json"함수는 오류를 반환하고 "try_parse_json"함수는 NULL 값을 반환합니다.

INSERT INTO test VALUES
  (1, parse_json('{"a":{"b":1,"c":[1,2]}}'), parse_json('[["a","b"],{"k":"a"}]')),
  (2, parse_json('{"a":{"b":2,"c":[3,4]}}'), parse_json('[["c","d"],{"k":"b"}]'));
SELECT * FROM test;
+----+-------------------------+-----------------------+
| id | v1                      | v2                    |
+----+-------------------------+-----------------------+
| 1  | {"a":{"b":1,"c":[1,2]}} | [["a","b"],{"k":"a"}] |
| 2  | {"a":{"b":2,"c":[3,4]}} | [["c","d"],{"k":"b"}] |
+----+-------------------------+-----------------------+


JSON은 일반적으로 배열 또는 객체 유형의 데이터를 보유합니다. 중첩된 계층 구조로 인해 JSON PATH를 통해 내부 요소에 액세스할 수 있습니다. 구문은 다음 구분 기호를 지원합니다.

":": 콜론은 키로 개체의 요소를 가져오는 데 사용할 수 있습니다.
".": 점을 사용하여 키로 개체의 요소를 가져올 수 있습니다. 문에서 첫 번째 구분 기호로 점을 사용하지 마십시오. 그렇지 않으면 Databend가 점을 구분 기호로 간주하여 테이블 이름과 열 이름을 구분합니다.
"[]": 대괄호를 사용하여 키로 개체의 요소를 가져오거나 인덱스로 배열의 요소를 가져올 수 있습니다.
위의 세 가지 유형의 구분 기호를 혼합할 수 있습니다.

SELECT v1:a.c, v1:a['b'], v1['a']:c, v2[0][1], v2[1].k FROM test;
+--------+-----------+-----------+----------+---------+
| v1:a.c | v1:a['b'] | v1['a']:c | v2[0][1] | v2[1].k |
+--------+-----------+-----------+----------+---------+
| [1,2]  | 1         | [1,2]     | "b"      | "a"     |
| [3,4]  | 2         | [3,4]     | "d"      | "b"     |
+--------+-----------+-----------+----------+---------+


JSON PATH를 통해 추출된 내부 요소도 JSON 타입이며, 변환 연산자 “::”를 사용하거나 cast 함수를 통해 기본 타입으로 변환할 수 있습니다.

SELECT cast(v1:a.c[0], int64), v1:a.b::int32, v2[0][1]::string FROM test;
+--------------------------+---------------+------------------+
| cast(v1:a.c[0] as int64) | v1:a.b::int32 | v2[0][1]::string |
+--------------------------+---------------+------------------+
| 1                        | 1             | b                |
| 3                        | 2             | d                |
+--------------------------+---------------+------------------+


GitHub에서 JSON 파싱



많은 공개 데이터 세트는 JSON 형식으로 저장됩니다. 구문 분석을 위해 이러한 데이터를 Databend로 가져올 수 있습니다. 다음 소개에서는 GitHub 이벤트 데이터 세트를 예로 사용합니다.

GitHub 이벤트 데이터 세트(GH 아카이브에서 다운로드)는 다음 JSON 형식을 사용합니다.

{
  "id":"23929425917",
  "type":"PushEvent",
  "actor":{
    "id":109853386,
    "login":"teeckyar-bot",
    "display_login":"teeckyar-bot",
    "gravatar_id":"",
    "url":"https://api.github.com/users/teeckyar-bot",
    "avatar_url":"https://avatars.githubusercontent.com/u/109853386?"
  },
  "repo":{
    "id":531248561,
    "name":"teeckyar/Times",
    "url":"https://api.github.com/repos/teeckyar/Times"
  },
  "payload":{
    "push_id":10982315959,
    "size":1,
    "distinct_size":1,
    "ref":"refs/heads/main",
    "head":"670e7ca4085e5faa75c8856ece0f362e56f55f09",
    "before":"0a2871cb7e61ce47a6790adaf09facb6e1ef56ba",
    "commits":[
      {
        "sha":"670e7ca4085e5faa75c8856ece0f362e56f55f09",
        "author":{
          "email":"[email protected]",
          "name":"teeckyar-bot"
        },
        "message":"1662804002 Timehash!",
        "distinct":true,
        "url":"https://api.github.com/repos/teeckyar/Times/commits/670e7ca4085e5faa75c8856ece0f362e56f55f09"
      }
    ]
  },
  "public":true,
  "created_at":"2022-09-10T10:00:00Z",
  "org":{
    "id":106163581,
    "login":"teeckyar",
    "gravatar_id":"",
    "url":"https://api.github.com/orgs/teeckyar",
    "avatar_url":"https://avatars.githubusercontent.com/u/106163581?"
  }
}


위의 데이터에서 "actor", "repo", "payload"및 "org"필드가 중첩된 구조를 가지며 JSON으로 저장될 수 있음을 알 수 있습니다. 다른 유형은 기본 데이터 유형으로 저장할 수 있습니다. 따라서 다음과 같은 테이블을 만들 수 있습니다.

CREATE TABLE `github_data` 
             ( 
                          `id`   VARCHAR, 
                          `type` VARCHAR, 
                          `actor` JSON, 
                          `repo` JSON, 
                          `payload` JSON, 
                          `public` BOOLEAN, 
                          `created_at` timestamp(0), 
                          `org` json 
             );


COPY INTO 명령을 사용하여 데이터를 로드합니다.

COPY INTO github_data
FROM 'https://data.gharchive.org/2022-09-10-10.json.gz'
FILE_FORMAT = (
  compression = auto
  type = NDJSON
);


다음 코드는 커밋이 가장 많은 상위 10개 프로젝트를 반환합니다.

SELECT   repo:name, 
         count(id) 
FROM     github_data 
WHERE    type = 'PushEvent' 
GROUP BY repo:name 
ORDER BY count(id) DESC 
LIMIT    10;
+----------------------------------------------------------+-----------+
| repo:name                                                | count(id) |
+----------------------------------------------------------+-----------+
| "Lombiq/Orchard"                                         | 1384      |
| "maique/microdotblog"                                    | 970       |
| "Vladikasik/statistic"                                   | 738       |
| "brokjad/got_config"                                     | 592       |
| "yanonono/booth-update"                                  | 537       |
| "networkoperator/demo-cluster-manifests"                 | 433       |
| "kn469/web-clipper-bed"                                  | 312       |
| "ufapg/jojo"                                             | 306       |
| "bj5nj7oh/bj5nj7oh"                                      | 291       |
| "appseed-projects2/500f32d3-8019-43ee-8f2a-a273163233fb" | 247       |
+----------------------------------------------------------+-----------+


다음 코드는 포크가 가장 많은 상위 10명의 사용자를 반환합니다.

SELECT   actor:login, 
         count(id) 
FROM     github_data 
WHERE    type='ForkEvent' 
GROUP BY actor:login 
ORDER BY count(id) DESC 
LIMIT    10;
+-----------------------------------+-----------+
| actor:login                       | count(id) |
+-----------------------------------+-----------+
| "actions-marketplace-validations" | 191       |
| "alveraboquet"                    | 59        |
| "ajunlonglive"                    | 50        |
| "Shutch420"                       | 13        |
| "JusticeNX"                       | 13        |
| "RyK-eR"                          | 12        |
| "DroneMad"                        | 10        |
| "UnqulifiedEngineer"              | 9         |
| "PeterZs"                         | 8         |
| "lgq2015"                         | 8         |
+-----------------------------------+-----------+


성능 최적화



JSON 데이터는 일반적으로 일반 텍스트 형식으로 저장되며 데이터를 읽을 때마다 serde_json::Value의 열거형 값을 생성하기 위해 구문 분석해야 합니다. 다른 기본 데이터 유형과 비교할 때 JSON 데이터를 처리하는 데 더 많은 구문 분석 시간이 걸리고 더 많은 메모리 공간이 필요합니다.

Databend는 다음 방법을 사용하여 JSON 데이터의 읽기 성능을 개선했습니다.
  • 구문 분석 속도를 높이고 메모리 사용량을 줄이기 위해 Databend는 JSON 데이터를 바이너리 형식의 JSONB로 저장하고 내장된 j_entry 구조를 사용하여 각 요소의 데이터 유형 및 오프셋 위치를 유지합니다.
  • 쿼리 속도를 높이기 위해 가상 열을 추가합니다. Databend는 자주 쿼리되는 필드와 동일한 데이터 유형의 필드를 추출하여 별도의 가상 열로 저장합니다. 데이터는 쿼리할 때 가상 열에서 직접 읽히므로 Databend는 다른 기본 데이터 유형을 쿼리하는 것과 동일한 성능을 달성합니다.
  • 좋은 웹페이지 즐겨찾기