SQL 메타데이터 테이블

SQL 메타데이터 테이블

Druid의 시스템 테이블인 INFORMATION_SCHEMA와 sys 스키마를 소개해요. 각 데이터소스의 테이블·컬럼 메타데이터와 Druid 내부(segment/task/server) 정보를 SQL로 조회하는 방법을 설명해요.

출처: 문서

본문

Apache Druid는 Druid SQL과 native 쿼리 두 가지 쿼리 언어를 지원해요. 이 문서는 SQL 언어를 설명해요.

Druid Broker는 클러스터에 로드된 segment에서 각 데이터소스의 테이블·컬럼 메타데이터를 추론하고, 이를 사용해 SQL 쿼리를 계획해요. 이 메타데이터는 Broker 시작 시 캐시되고, SegmentMetadata 쿼리를 통해 백그라운드에서 주기적으로 갱신돼요. 백그라운드 메타데이터 갱신은 segment의 클러스터 진입·이탈에 의해 트리거되며, 구성(configuration)을 통해 조절(throttle)할 수도 있어요.

Druid는 특수한 시스템 테이블을 통해 시스템 정보를 노출해요. 사용 가능한 스키마는 두 가지가 있어요: Information Schema와 Sys Schema예요. Information schema는 테이블과 컬럼 타입에 대한 세부 정보를 제공하고, "sys" 스키마는 segment/task/server 같은 Druid 내부 정보를 제공해요.

INFORMATION SCHEMA

JDBC에서 connection.getMetaData() 를 사용하거나, 아래 설명하는 INFORMATION_SCHEMA 테이블을 통해 테이블·컬럼 메타데이터에 접근할 수 있어요. 예를 들어 Druid 데이터소스 "foo"의 메타데이터를 가져오려면 다음 쿼리를 사용해요:

SELECT *FROM INFORMATION_SCHEMA.COLUMNSWHERE "TABLE_SCHEMA" = 'druid' AND "TABLE_NAME" = 'foo'

참고: INFORMATION_SCHEMA 테이블은 현재 TIME_PARSE 나 APPROX_QUANTILE_DS 같은 Druid 전용 함수를 지원하지 않아요. 표준 SQL 함수만 사용할 수 있어요.

SCHEMATA 테이블

INFORMATION_SCHEMA.SCHEMATA는 알려진 모든 스키마의 목록을 제공해요. 여기에는 표준 Druid Table 데이터소스용 druid , Lookups용 lookup , 가상 System 메타데이터 테이블용 sys , 그리고 이 가상 테이블들용 INFORMATION_SCHEMA 가 포함돼요. 서로 다른 스키마에서 테이블 이름이 같을 수 있으므로, SQL 문장에 스키마를 포함해 구분할 수 있어요. 예: lookup.table 대 druid.table .

| Column | Type | Notes | | CATALOG_NAME | VARCHAR | 항상 druid 로 설정 | | SCHEMA_NAME | VARCHAR | druid , lookup , sys 또는 INFORMATION_SCHEMA | | SCHEMA_OWNER | VARCHAR | 사용되지 않음 | | DEFAULT_CHARACTER_SET_CATALOG | VARCHAR | 사용되지 않음 | | DEFAULT_CHARACTER_SET_SCHEMA | VARCHAR | 사용되지 않음 | | DEFAULT_CHARACTER_SET_NAME | VARCHAR | 사용되지 않음 | | SQL_PATH | VARCHAR | 사용되지 않음 |

TABLES 테이블

INFORMATION_SCHEMA.TABLES는 알려진 모든 테이블과 스키마의 목록을 제공해요.

| Column | Type | Notes | | TABLE_CATALOG | VARCHAR | 항상 druid 로 설정 | | TABLE_SCHEMA | VARCHAR | 테이블이 속한 'schema'. 자세한 내용은 SCHEMATA 테이블 참고 | | TABLE_NAME | VARCHAR | 테이블 이름. druid 스키마에서는 이 값이 dataSource 예요. | | TABLE_TYPE | VARCHAR | "TABLE" 또는 "SYSTEM_TABLE" | | IS_JOINABLE | VARCHAR | 서브쿼리 수행 없이 JOIN 문의 오른쪽에 있을 때 직접 join 가능한 테이블이면 YES , 아니면 NO 로 설정돼요. Lookups은 Druid 쿼리 처리 노드에 전역적으로 분산되어 있으므로 항상 join 가능하지만, Druid 데이터소스는 그렇지 않아 덜 효율적인 서브쿼리 join을 사용해요. | | IS_BROADCAST | VARCHAR | 테이블이 'broadcast'로 모든 Druid 쿼리 처리 노드에 분산되어 있으면 YES 로 설정돼요. 예를 들어 lookups과 'broadcast' 로드 규칙이 있는 Druid 데이터소스가 해당돼요. 그 외에는 NO 예요. |

COLUMNS 테이블

INFORMATION_SCHEMA.COLUMNS는 모든 테이블과 스키마의 모든 알려진 컬럼 목록을 제공해요.

| Column | Type | Notes | | TABLE_CATALOG | VARCHAR | 항상 druid 로 설정 | | TABLE_SCHEMA | VARCHAR | 테이블 컬럼이 속한 'schema'. 자세한 내용은 SCHEMATA 테이블 참고 | | TABLE_NAME | VARCHAR | 컬럼이 속한 'table'. 자세한 내용은 TABLES 테이블 참고 | | COLUMN_NAME | VARCHAR | 컬럼 이름 | | ORDINAL_POSITION | BIGINT | 컬럼이 테이블에 저장된 순서 | | COLUMN_DEFAULT | VARCHAR | 사용되지 않음 | | IS_NULLABLE | VARCHAR | | | DATA_TYPE | VARCHAR | | | CHARACTER_MAXIMUM_LENGTH | BIGINT | 사용되지 않음 | | CHARACTER_OCTET_LENGTH | BIGINT | 사용되지 않음 | | NUMERIC_PRECISION | BIGINT | | | NUMERIC_PRECISION_RADIX | BIGINT | | | NUMERIC_SCALE | BIGINT | | | DATETIME_PRECISION | BIGINT | | | CHARACTER_SET_NAME | VARCHAR | | | COLLATION_NAME | VARCHAR | | | JDBC_TYPE | BIGINT | java.sql.Types 의 타입 코드 (Druid 확장) |

예를 들어, 다음 쿼리는 foo 테이블 컬럼의 데이터 타입 정보를 반환해요:

SELECT "ORDINAL_POSITION", "COLUMN_NAME", "IS_NULLABLE", "DATA_TYPE", "JDBC_TYPE"FROM INFORMATION_SCHEMA.COLUMNSWHERE "TABLE_NAME" = 'foo'

ROUTINES 테이블

INFORMATION_SCHEMA.ROUTINES는 알려진 모든 함수의 목록을 제공해요.

| Column | Type | Notes | | ROUTINE_CATALOG | VARCHAR | 루틴을 포함하는 catalog. 항상 druid 로 설정 | | ROUTINE_SCHEMA | VARCHAR | 루틴을 포함하는 schema. 항상 INFORMATION_SCHEMA 로 설정 | | ROUTINE_NAME | VARCHAR | 루틴 이름 | | ROUTINE_TYPE | VARCHAR | 루틴 타입. 항상 FUNCTION 으로 설정 | | IS_AGGREGATOR | VARCHAR | 루틴이 집계 함수이면 YES , 아니면 NO 로 설정 | | SIGNATURES | VARCHAR | 하나 이상의 루틴 시그니처 |

예를 들어, 다음 쿼리는 모든 집계 함수에 대한 정보를 반환해요:

SELECT "ROUTINE_CATALOG", "ROUTINE_SCHEMA", "ROUTINE_NAME", "ROUTINE_TYPE", "IS_AGGREGATOR", "SIGNATURES"FROM "INFORMATION_SCHEMA"."ROUTINES"WHERE "IS_AGGREGATOR" = 'YES'

SYSTEM SCHEMA

"sys" 스키마는 Druid의 segment, server, task를 들여다볼 수 있게 해줘요.

참고: "sys" 테이블은 현재 TIME_PARSE 나 APPROX_QUANTILE_DS 같은 Druid 전용 함수를 지원하지 않아요. 표준 SQL 함수만 사용할 수 있어요.

SEGMENTS 테이블

Segments 테이블은 아직 게시(publish)되었는지 여부와 관계없이 모든 Druid segment에 대한 세부 정보를 제공해요.

| Column | Type | Notes | | segment_id | VARCHAR | 고유 segment 식별자 | | datasource | VARCHAR | 데이터소스 이름 | | start | VARCHAR | interval 시작 시간 (ISO 8601 형식) | | end | VARCHAR | interval 종료 시간 (ISO 8601 형식) | | size | BIGINT | segment 크기 (바이트) | | version | VARCHAR | 버전 문자열 (일반적으로 segment 집합이 처음 시작된 시점에 해당하는 ISO8601 타임스탬프). 버전이 높을수록 더 최근에 생성된 segment예요. 버전 비교는 문자열 비교에 기반해요. | | partition_num | BIGINT | 파티션 번호 (정수, datasource+interval+version 내에서 고유하며 반드시 연속적이지 않을 수 있음) | | num_replicas | BIGINT | 현재 서빙 중인 이 segment의 복제본 수 | | num_rows | BIGINT | 이 segment의 행 수. 행 수를 알 수 없으면 0. 이 행 수는 Broker가 백그라운드에서 수집해요. Broker가 아직 이 segment의 행 수를 수집하지 않았다면 0이 돼요. 스트림에서 수집된 segment의 경우, Broker의 캐시된 num_rows 가 오래되었을 수 있어 보고된 행 수가 count(*) 쿼리 결과보다 늦을 수 있어요. 해당 segment에 새 행 쓰기가 중단된 직후에 안정화돼요. | | is_active | BIGINT | 데이터소스의 최신 상태를 나타내는 segment면 true. (is_published = 1 AND is_overshadowed = 0) OR is_realtime = 1 과 동일해요. 수집이나 데이터 관리 작업이 없는 안정 상태에서는 is_active 가 is_available 과 동일해요. 그러나 최근에 수집이나 데이터 관리 작업이 실행된 경우에는 서로 다를 수 있어요. 이런 경우 Druid는 is_active 가 주는 기대 상태에 맞춰 실제 가용성을 맞추도록 segment를 적절히 로드·언로드해요. | | is_published | BIGINT | long 타입으로 표현된 불리언 (1 = true, 0 = false). segment가 메타데이터 저장소에 게시되고 used로 표시되면 1. 자세한 내용은 segment lifecycle 문서 참고. | | is_available | BIGINT | long 타입으로 표현된 불리언 (1 = true, 0 = false). segment가 현재 Historical이나 실시간 수집 task 같은 어떤 데이터 서빙 프로세스에 의해 서빙되고 있으면 1. 자세한 내용은 segment lifecycle 문서 참고. | | is_realtime | BIGINT | long 타입으로 표현된 불리언 (1 = true, 0 = false). segment가 실시간 task에 의해서만 서빙되면 1, 어떤 Historical 프로세스가 이 segment를 서빙하면 0. | | is_overshadowed | BIGINT | long 타입으로 표현된 불리언 (1 = true, 0 = false). segment가 게시되었고 다른 게시된 segment들에 의해 완전히 가려지면 1. 현재는 게시되지 않은 segment에 대해 항상 0이지만, 미래에는 바뀔 수 있어요. is_published = 1 AND is_overshadowed = 0 으로 필터링해 "게시되어야 하는" segment를 찾을 수 있어요. segment는 최근에 교체되었지만 아직 게시 취소되지 않았다면 잠시 게시됨과 동시에 가려짐 상태일 수 있어요. 자세한 내용은 segment lifecycle 문서 참고. | | shard_spec | VARCHAR | segment ShardSpec 의 JSON 직렬화 형식 | | dimensions | VARCHAR | segment dimensions의 JSON 직렬화 형식 | | metrics | VARCHAR | segment metrics의 JSON 직렬화 형식 | | last_compaction_state | VARCHAR | 이 segment를 만든 compaction task config의 JSON 직렬화 형식. compaction task가 만든 segment가 아니면 null 일 수 있어요. | | replication_factor | BIGINT | 현재 이 segment에 적용되는 로드 규칙에 따라 모든 historical tier에 걸쳐 로드되어야 하는 segment 복제본의 총 수. 이 값이 0이면 segment가 어떤 historical에도 할당되지 않아 로드되지 않아요. segment의 로드 규칙이 아직 평가되지 않았다면 이 값은 -1 이에요. |

예를 들어, 데이터소스 "wikipedia"의 현재 활성 segment를 모두 가져오려면 다음 쿼리를 사용해요:

SELECT * FROM sys.segmentsWHERE datasource = 'wikipedia'AND is_active = 1

데이터소스별 segment total_size, avg_size, avg_num_rows와 num_segments를 가져오는 또 다른 예시:

SELECT    datasource,    SUM("size") AS total_size,    CASE WHEN SUM("size") = 0 THEN 0 ELSE SUM("size") / (COUNT(*) FILTER(WHERE "size" > 0)) END AS avg_size,    CASE WHEN SUM(num_rows) = 0 THEN 0 ELSE SUM("num_rows") / (COUNT(*) FILTER(WHERE num_rows > 0)) END AS avg_num_rows,    COUNT(*) AS num_segmentsFROM sys.segmentsWHERE is_active = 1GROUP BY 1ORDER BY 2 DESC

이 쿼리는 한 단계 더 나아가, foo 데이터소스에 대해 각 백만 행 버킷별 사용 가능하고 실시간이 아닌 segment의 전체 프로필을 보여줘요:

SELECT ABS("num_rows" /  1000000) as "bucket",  COUNT(*) as segments,  SUM("size") / 1048576 as totalSizeMiB,  MIN("size") / 1048576 as minSizeMiB,  AVG("size") / 1048576 as averageSizeMiB,  MAX("size") / 1048576 as maxSizeMiB,  SUM("num_rows") as totalRows,  MIN("num_rows") as minRows,  AVG("num_rows") as averageRows,  MAX("num_rows") as maxRows,  (AVG("size") / AVG("num_rows"))  as avgRowSizeBFROM sys.segmentsWHERE is_available = 1 AND is_realtime = 0 AND "datasource" = `foo`GROUP BY 1ORDER BY 1

압축된(compaction, 어떤 것이든) segment를 가져오려면:

SELECT * FROM sys.segments WHERE is_active = 1 AND last_compaction_state IS NOT NULL

또는 특정 compaction spec(예: 자동 압축)에 의해서만 압축된 segment를 가져오려면:

SELECT * FROM sys.segments WHERE is_active = 1 AND last_compaction_state = 'CompactionState{partitionsSpec=DynamicPartitionsSpec{maxRowsPerSegment=5000000, maxTotalRows=9223372036854775807}, indexSpec={bitmap={type=roaring}, dimensionCompression=lz4, metricCompression=lz4, longEncoding=longs, segmentLoader=null}}'

SERVERS 테이블

Servers 테이블은 클러스터에서 발견된 모든 서버를 나열해요.

| Column | Type | Notes | | server | VARCHAR | host :port 형식의 서버 이름 | | host | VARCHAR | 서버의 호스트 이름 | | plaintext_port | BIGINT | 서버의 비보안 포트. plaintext 트래픽이 비활성화되면 -1 | | tls_port | BIGINT | 서버의 TLS 포트. TLS가 비활성화되면 -1 | | server_type | VARCHAR | Druid 서비스 타입. 가능한 값: COORDINATOR, OVERLORD, BROKER, ROUTER, HISTORICAL, MIDDLE_MANAGER 또는 PEON. | | tier | VARCHAR | 유통 tier. druid.server.tier 참고. HISTORICAL 타입에만 유효하며, 다른 타입은 null | | current_size | BIGINT | 이 서버의 현재 segment 크기(바이트). HISTORICAL 타입에만 유효하며, 다른 타입은 0 | | max_size | BIGINT | 이 서버가 segment에 할당을 권장하는 최대 크기(바이트). druid.server.maxSize 참고. HISTORICAL 타입에만 유효하며, 다른 타입은 0 | | is_leader | BIGINT | 서버가 현재 'leader'이면 1(리더십 개념이 있는 서비스의 경우), 아니면 0, 서버 타입에 리더십 개념이 없으면 null | | start_time | STRING | 서버가 클러스터에 announce된 시점의 ISO8601 형식 타임스탬프 | | version | VARCHAR | 서버에서 실행 중인 Druid 버전 | | build_revision | VARCHAR | 서버 바이너리를 만든 빌드의 git 커밋 | | labels | VARCHAR | druid.labels 속성으로 구성된 서버의 labels | | available_processors | BIGINT | 서버에서 사용 가능한 총 CPU 프로세서 수 | | total_memory | BIGINT | 서버에서 사용 가능한 총 메모리(바이트) |

모든 서버에 대한 정보를 가져오려면 다음 쿼리를 사용해요:

SELECT * FROM sys.servers;

SERVER_SEGMENTS 테이블

SERVER_SEGMENTS는 servers 테이블과 segments 테이블을 join하는 데 사용돼요.

| Column | Type | Notes | | server | VARCHAR | host :port 형식의 서버 이름 (servers 테이블 의 기본 키) | | segment_id | VARCHAR | segment 식별자 (segments 테이블 의 기본 키) |

"servers"와 "segments" 간의 JOIN을 사용해 특정 데이터소스의 segment 수를 서버별로 그룹화해 쿼리할 수 있어요. 예시:

SELECT count(segments.segment_id) as num_segments from sys.segments as segmentsINNER JOIN sys.server_segments as server_segmentsON segments.segment_id  = server_segments.segment_idINNER JOIN sys.servers as serversON servers.server = server_segments.serverWHERE segments.datasource = 'wikipedia'GROUP BY servers.server;

TASKS 테이블

tasks 테이블은 활성 task와 최근 완료된 task에 대한 정보를 제공해요. 자세한 내용은 ingestion tasks 문서를 참고해요.

| Column | Type | Notes | | task_id | VARCHAR | 고유 task 식별자 | | group_id | VARCHAR | 이 task의 task group ID. 값은 task type 에 따라 달라져요. 예를 들어 native index task의 경우 task_id 와 같고, sub task의 경우 이 값은 부모 task의 ID예요 | | type | VARCHAR | task 타입. 예를 들어 indexing task의 경우 "index". tasks-overview 참고 | | datasource | VARCHAR | indexing되는 데이터소스 이름 | | created_time | VARCHAR | ingestion task가 생성된 시점의 ISO8601 형식 타임스탬프. 완료·대기 task에는 값이 채워지고, 실행 중·보류 중 task에는 1970-01-01T00:00:00Z 로 설정돼요 | | queue_insertion_time | VARCHAR | 이 task가 Overlord의 큐에 추가된 시점의 ISO8601 형식 타임스탬프 | | status | VARCHAR | task 상태: RUNNING, FAILED, SUCCESS | | runner_status | VARCHAR | 완료된 task의 runner 상태는 NONE, 진행 중인 task는 RUNNING, WAITING, PENDING일 수 있어요 | | duration | BIGINT | task를 완료하는 데 걸린 시간(밀리초). 완료된 task에만 존재 | | location | VARCHAR | task가 실행 중인 서버 이름 ( host :port 형식). RUNNING task에만 존재 | | host | VARCHAR | task가 실행 중인 서버의 호스트 이름 | | plaintext_port | BIGINT | 서버의 비보안 포트. plaintext 트래픽이 비활성화되면 -1 | | tls_port | BIGINT | 서버의 TLS 포트. TLS가 비활성화되면 -1 | | error_msg | VARCHAR | FAILED task의 자세한 오류 메시지 |

예를 들어, 상태로 필터링된 task 정보를 가져오려면 다음 쿼리를 사용해요:

SELECT * FROM sys.tasks WHERE status='FAILED';

SUPERVISORS 테이블

supervisors 테이블은 supervisor에 대한 정보를 제공해요.

| Column | Type | Notes | | supervisor_id | VARCHAR | supervisor task 식별자 | | datasource | VARCHAR | supervisor가 작업하는 데이터소스 | | state | VARCHAR | supervisor의 기본 상태. 가능한 상태: UNHEALTHY_SUPERVISOR , UNHEALTHY_TASKS , PENDING , RUNNING , SUSPENDED , STOPPING . 자세한 내용은 Supervisor reference 참고 | | detailed_state | VARCHAR | supervisor 특정 상태. 특정 supervisor 문서 참고: Kafka 또는 Kinesis | | healthy | BIGINT | long 타입으로 표현된 불리언 (1 = true, 0 = false). 1은 건강한 supervisor를 나타내요 | | type | VARCHAR | supervisor 타입. 예: kafka , kinesis 또는 materialized_view | | source | VARCHAR | supervisor의 소스. 예: Kafka topic 또는 Kinesis stream | | suspended | BIGINT | long 타입으로 표현된 불리언 (1 = true, 0 = false). 1은 supervisor가 일시 중지된 상태임을 나타내요 | | spec | VARCHAR | JSON 직렬화된 supervisor spec |

예를 들어, 건강 상태로 필터링된 supervisor task 정보를 가져오려면 다음 쿼리를 사용해요:

SELECT * FROM sys.supervisors WHERE healthy=0;

SERVER_PROPERTIES 테이블

server_properties 테이블은 각 Druid 서버에 구성된 runtime properties를 노출해요. 각 행은 특정 서버와 연결된 단일 속성 키-값 쌍을 나타내요.

| Column | Type | Notes | | server | VARCHAR | 서버의 host와 port ( host:port 형식) | | service_name | VARCHAR | druid.service 로 정의된 서버의 서비스 이름 | | node_roles | VARCHAR | 서버가 수행하는 역할의 쉼표로 구분된 목록. 예를 들어 서버가 Coordinator와 Overlord로 모두 기능하면 [coordinator,overlord] | | property | VARCHAR | 속성 이름 | | value | VARCHAR | 속성 값 |

예를 들어, 특정 서버의 속성을 가져오려면 다음 쿼리를 사용해요:

SELECT * FROM sys.server_properties WHERE server='192.168.1.1:8081'

QUERIES 테이블

sys.queries 테이블은 실험적 기능이에요. Broker 프로세스에서 runtime property druid.sql.planner.enableSysQueriesTable=true 를 설정해 활성화해야 해요. 이 테이블이 실험적인 주된 이유는, 역시 실험적인 Dart 엔진의 쿼리만 보여주기 때문이에요.

queries 테이블은 현재 실행 중이거나 최근 완료된 SQL 쿼리에 대한 정보를 제공해요.

| Column | Type | Notes | | id | VARCHAR | 쿼리의 실행 ID. Dart 쿼리의 경우 이 값은 dartQueryId 예요. | | engine | VARCHAR | 쿼리를 실행한 SQL 엔진. 예: msq-dart | | state | VARCHAR | 쿼리 상태: ACCEPTED , RUNNING , SUCCESS , FAILED 또는 CANCELED | | info | VARCHAR | sqlQueryId , sql , identity , startTime 및 기타 엔진별 세부 정보를 포함한 JSON 직렬화 쿼리 정보 |

예를 들어, 최근 완료된 모든 Dart 쿼리를 가져오려면:

SELECT *FROM sys.queriesWHERE  engine = 'msq-dart'  AND state IN ('SUCCESS', 'FAILED', 'CANCELED')

완료된 쿼리 정보의 보존은 Dart controller 구성에 의해 제어돼요. 완료된 쿼리가 얼마나 오래 보존되는지에 대한 자세한 내용은 druid.msq.dart.controller.maxRetainedReportCount 와 druid.msq.dart.controller.maxRetainedReportDuration 을 참고해요.

더 알아보기 (Learn more)