딥 스토리지에서 쿼리하기

딥 스토리지에서 쿼리하기 (Query from deep storage)

딥 스토리지(deep storage, 심층 저장소)에만 저장된 세그먼트도 Druid로 쿼리할 수 있어요. 자주 접근하지 않는 데이터나 낮은 지연이 꼭 필요하지 않은 데이터에 유용한 기능이에요. Historical을 늘리지 않고도 쿼리할 수 있는 데이터 영역을 넓힐 수 있어요.

출처: 문서

본문

Druid는 딥 스토리지에만 저장된 세그먼트를 쿼리할 수 있어요. 딥 스토리지에서 쿼리를 실행하는 것은 Historical 프로세스에 로드된 세그먼트에서 쿼리하는 것보다 느리지만, 자주 접근하지 않거나 전형적인 Druid 쿼리가 제공하는 낮은 지연이 필요 없는 데이터에는 훌륭한 도구예요. 딥 스토리지에서의 쿼리는 더 많은 세그먼트를 수용하도록 Historical 프로세스를 확장하지 않아도 쿼리 가능한 데이터의 영역을 넓힐 수 있어요.

전제 조건 (Prerequisites)

쿼리하려면 datasource가 다음 조건 중 하나를 충족해야 해요:

  • datasource의 세그먼트가 최소 하나는 Historical 서비스에 로드되어 있어야 Druid가 쿼리를 계획할 수 있어요. 이 세그먼트는 datasource의 어떤 세그먼트든 괜찮아요. Druid 콘솔에서 볼 수 있다면 datasource에 Historical 서비스에 최소 하나의 세그먼트가 있다는 것을 확인할 수 있어요.
  • 중앙화된 datasource 스키마(centralized datasource schema) 기능이 활성화되어 있어야 해요. 자세한 내용은 Centralized datasource schema 를 참고하세요.

중앙화된 datasource 스키마를 사용한다면, 기능을 활성화하기 전에 만들어진 datasource에 대해 딥 스토리지에서 쿼리 가능하게 만들기 위한 추가 단계가 있어요. 스키마가 메타데이터 데이터베이스에 백필(backfill)될 수 있도록 딥 스토리지의 세그먼트를 Historical에 로드해야 해요. 딥 스토리지에만 있는 세그먼트 중 일부 또는 전부를 로드할 수 있어요. 모든 세그먼트를 로드하지 않으면, 로드하지 않은 세그먼트에만 있는 차원은 쿼리 가능한 datasource 스키마에 없어서 딥 스토리지에서 쿼리할 수 없어요. 즉 메타데이터 데이터베이스의 세그먼트 스키마에 있는 차원만 쿼리할 수 있어요. 이 과정이 끝나면 Historical에서 모든 세그먼트를 내려(unload) 딥 스토리지에만 데이터를 유지할 수 있어요.

세그먼트를 딥 스토리지에만 유지하기 (Keep segments in deep storage only)

Druid에 인제스트하는 모든 데이터는 이미 딥 스토리지에 저장되므로, 그런 관점에서는 추가 구성이 필요 없어요. 하지만 딥 스토리지 쿼리가 주는 비용 절감을 이용하려면 모든 세그먼트가 Historical 프로세스에 로드되지 않게 해야 해요. 중앙화된 datasource 스키마를 사용하면 datasource를 딥 스토리지에만 유지하면서도 쿼리 가능하게 할 수 있어요.

어떤 세그먼트를 딥 스토리지에만 유지하고 어떤 세그먼트를 Historical 프로세스에 로드할지는 load rules 로 관리해요.

세그먼트를 딥 스토리지에만 유지하는 가장 쉬운 방법은 Historical 프로세스에 로드되지 않을 세그먼트를 명시적으로 구성하는 거예요. tieredReplicants를 빈 배열로, useDefaultTierForNull을 false로 설정하세요. 예를 들어 datasource에 대해 다음 규칙을 구성하면:

[
  {
    "interval": "2016-06-27T00:00:00.000Z/2016-06-27T02:59:00.000Z",
    "tieredReplicants": {},
    "useDefaultTierForNull": false,
    "type": "loadByInterval"
  }
]

지정된 interval 안에 있는 모든 세그먼트는 딥 스토리지에만 존재해요. 이 interval에 없는 세그먼트는 기본 클러스터 load rules나 구성한 다른 load rules를 사용해요.

Druid 콘솔에서 load rules를 구성하려면 Datasources > ... (Actions 컬럼) > Edit retention rules 로 이동한 뒤, 제공된 JSON을 JSON 탭에 붙여넣으세요.

세그먼트가 어떤 Historical 티어에도 로드되지 않았는지 Druid 메타데이터 테이블을 쿼리해 확인할 수 있어요:

SELECT "segment_id", "replication_factor"
FROM sys."segments"
WHERE "replication_factor" = 0 AND "datasource" = YOUR_DATASOURCE

replication_factor가 0인 세그먼트는 어떤 Historical 티어에도 할당되지 않아요. 이 세그먼트에 대한 쿼리는 딥 스토리지의 세그먼트에 직접 실행돼요.

Druid 콘솔에서도 확인할 수 있어요. Segments 페이지에서 Replication factor 컬럼을 보세요.

Druid가 load rules를 처리하는 동안 실제 복제본 수가 일시적으로 replication factor와 다를 수 있다는 점을 유의하세요.

딥 스토리지에서 쿼리 실행하기 (Run a query from deep storage)

쿼리 제출 (Submit a query)

POST /sql/statements API나 Druid 콘솔에 쿼리를 제출해 딥 스토리지의 데이터를 쿼리할 수 있어요. Druid는 multi-stage query (MSQ) 태스크 엔진으로 쿼리를 수행해요.

딥 스토리지에서 쿼리를 실행하려면 POST 메서드로 Router에 쿼리를 보내세요:

POST https://ROUTER:8888/druid/v2/sql/statements

딥 스토리지에서 쿼리를 제출하는 것은 다른 Druid SQL 쿼리와 동일한 문법을 사용해요. 쿼리는 요청 페이로드의 JSON 객체 안의 "query" 필드에 담겨요. 예:

{"query" : "SELECT COUNT(*) FROM data_source WHERE foo = 'bar'"}

일반적으로 sql과 sql/statements 엔드포인트 간 요청 본문 필드는 동일해요.

여기서 언급한 context 파라미터 외에 sql/statements에는 추가 context 파라미터가 있어요:

  • executionMode (필수) — 쿼리 결과를 어떻게 가져올지 결정해요. ASYNC로 설정하세요.
  • selectDestination (선택) — durableStorage로 설정하면 SELECT 쿼리 결과를 내구성 있는 스토리지(durable storage)에 쓰라고 Druid에 지시해요. 결과 집합이 3000행을 넘는 경우 durableStorage를 사용하는 것이 강력히 권장돼요. 이 기능을 사용하려면 MSQ용 durable storage가 활성화 되어 있어야 해요.

딥 스토리지 쿼리가 지원하는 두 가지 추가 context 파라미터가 포함된 샘플 쿼리는 다음과 같아요:

curl --location 'http://localhost:8888/druid/v2/sql/statements' \
--header 'Content-Type: application/json' \
--data '{
    "query":"SELECT * FROM \"YOUR_DATASOURCE\" where \"__time\" > TIMESTAMP '2017-09-01' and \"__time\" <= TIMESTAMP '2017-09-02'",
    "context":{
        "executionMode":"ASYNC",
        "selectDestination": "durableStorage"
    }
  }'

SET을 사용해 context 파라미터를 제출할 수도 있다는 점을 유의하세요. 예:

"query": "SET executionMode = 'ASYNC'; SET selectDestination = 'durableStorage'; SELECT * FROM \"YOUR_DATASOURCE\" WHERE \"__time\" > TIMESTAMP '2017-09-01' AND \"__time\" <= TIMESTAMP '2017-09-02'"

쿼리 제출에 대한 응답에는 쿼리 ID와 함께 쿼리를 제출한 시각, 결과 스키마 같은 기본 정보가 포함돼요:

{
  "queryId": "query-ALPHANUMBERIC-STRING",
  "state": "ACCEPTED",
  "createdAt": CREATION_TIMESTAMP,
  "schema": [
    {
      "name": COLUMN_NAME,
      "type": COLUMN_TYPE,
      "nativeType": COLUMN_TYPE
    },
    ...
  ],
  "durationMs": DURATION_IN_MS,
}

쿼리 상태 가져오기 (Get query status)

다음 API 호출로 쿼리 상태를 확인할 수 있어요:

GET https://ROUTER:8888/druid/v2/sql/statements/QUERYID

쿼리는 ACCEPTED나 RUNNING 같은 쿼리 상태를 반환해요. 결과를 가져오기 전에 상태가 SUCCESS인지 확인하세요.

성공한 쿼리의 상태를 확인하면 샘플 레코드와 결과가 페이지 로 어떻게 정리되는지에 대한 정보를 포함해 쿼리 결과에 대한 유용한 정보가 포함돼 있어요. 각 페이지의 정보에는 다음이 포함돼요:

  • numRows : 해당 결과 페이지의 행 수
  • sizeInBytes : 페이지의 크기
  • id : 쿼리 결과를 가져올 때 특정 페이지를 참조하는 데 사용할 수 있는 인덱스된 페이지 번호

page 파라미터를 사용해 가져올 결과를 다듬을 수 있어요.

다음 스니펫은 result 객체의 구조를 보여줘요:

{
  ...
  "result": {
    "numTotalRows": INTEGER,
    "totalSizeInBytes": INTEGER,
    "dataSource": "__query_select",
    "sampleRecords": [
      [
        RECORD_1,
        RECORD_2,
        ...
      ]
    ],
    "pages": [
      {
        "numRows": INTEGER,
        "sizeInBytes": INTEGER,
        "id": INTEGER_PAGE_NUMBER
      }
      ...
    ]
  }
}

쿼리 결과 가져오기 (Get query results)

쿼리를 제출한 사용자만 그 쿼리의 결과를 가져올 수 있어요.

결과를 가져오려면 다음 엔드포인트를 사용하세요:

GET https://ROUTER:8888/druid/v2/sql/statements/QUERYID/results?page=PAGENUMBER&resultFormat=FORMAT

결과는 JSON 형식으로 반환돼요.

선택적인 page 파라미터로 결과를 다듬을 수 있고, resultFormat 파라미터로 결과가 표시될 형식을 정의할 수 있어요.

  • 완료된 쿼리의 상태를 가져와서 결과의 페이지 정보를 검색할 수 있어요.
  • resultFormat에 지원되는 옵션은 arrayLines, objectLines, array, object, csv 예요. 기본값은 object예요. 더 많은 문서는 여기 에 있어요.

딥 스토리지 쿼리에 대한 결과를 가져오려고 할 때 쿼리가 아직 실행 중이라는 오류를 받을 수 있어요. 다시 시도하기 전에 쿼리가 완료될 때까지 기다리세요.

더 읽어보기 (Further reading)

  • 딥 스토리지 쿼리 튜토리얼
  • 딥 스토리지 쿼리 API 레퍼런스

더 알아보기 (Learn more)