SQL JDBC 드라이버 API

SQL JDBC 드라이버 API

Avatica JDBC 드라이버로 Druid SQL 쿼리를 실행하는 방법을 다룹니다. 연결 문자열, 연결 고정(stickiness), 동적 파라미터, 그리고 실행 가능한 예제 코드를 보여줘요.

출처: 문서

본문

참고: Apache Druid는 두 가지 쿼리 언어를 지원합니다. Druid SQL과 **네이티브 쿼리(native queries)**예요. 이 문서는 SQL 언어를 설명합니다.

Avatica JDBC 드라이버를 사용해 Druid SQL 쿼리를 실행할 수 있습니다. Avatica JDBC 드라이버 1.23.0 이상 버전을 권장합니다. Avatica 1.21.0부터는 간헐적인 쿼리 실패가 보일 경우 transparent_reconnection 속성을 true로 설정해야 할 수도 있어요.

Avatica 클라이언트 jar를 다운로드한 뒤 클래스패스에 추가하세요.

연결 문자열 예시:

jdbc:avatica:remote:url=http://localhost:8888/druid/v2/sql/avatica/;transparent_reconnection=true

JSON 대신 protobuf 프로토콜을 사용하려면:

jdbc:avatica:remote:url=http://localhost:8888/druid/v2/sql/avatica-protobuf/;transparent_reconnection=true;serialization=protobuf

url은 Router의 /druid/v2/sql/avatica/ 엔드포인트로, JDBC 연결을 일관된 Broker로 라우팅합니다. 자세한 내용은 Connection stickiness를 참고하세요.

transparent_reconnection을 true로 설정하면 Broker 풀의 구성원이 바뀌거나 Broker가 재시작돼도 연결이 끊기지 않습니다.

protobuf 엔드포인트를 사용한다면 serialization을 protobuf로 설정하세요.

참고로 현재 시점(이 글을 쓰는 시점) 최신 버전인 Avatica 1.23.0은 JDBC 연결 문자열에서 Druid로 연결 컨텍스트 파라미터를 전달하는 것을 지원하지 않습니다. 이 컨텍스트 파라미터는 Properties 객체를 사용해 전달해야 해요. 아래 Java 코드 예시를 참고하세요.

Java 코드 예시:

// Connect to /druid/v2/sql/avatica/ on your Broker.
String url = "jdbc:avatica:remote:url=http://localhost:8888/druid/v2/sql/avatica/;transparent_reconnection=true";

// Set any connection context parameters you need here.
// Any property from https://druid.apache.org/docs/latest/querying/sql-query-context.html can go here.
Properties connectionProperties = new Properties();
connectionProperties.setProperty("sqlTimeZone", "Etc/UTC");
//To connect to a Druid deployment protected by basic authentication,
//you can incorporate authentication details from https://druid.apache.org/docs/latest/operations/security-overview   
connectionProperties.setProperty("user", "admin");                
connectionProperties.setProperty("password", "password1");     

try (Connection connection = DriverManager.getConnection(url, connectionProperties)) {
  try (
      final Statement statement = connection.createStatement();
      final ResultSet resultSet = statement.executeQuery(query)
  ) {
    while (resultSet.next()) {
      // process result set
    }
  }
}

실행할 수 있는 쿼리를 포함한 예시는 Examples 섹션을 참고하세요.

Druid와 함께 protocol buffers JDBC 연결을 사용하는 것도 가능합니다. 이 방식은 결과 집합이 클 때 불필요한 부분을 줄이고 잠재적 성능 향상을 가져와요. 사용하려면 아래 연결 URL을 대신 적용하면 되고, 나머지는 모두 동일합니다.

String url = "jdbc:avatica:remote:url=http://localhost:8888/druid/v2/sql/avatica-protobuf/;transparent_reconnection=true;serialization=protobuf";

참고: protobuf 엔드포인트는 공식 Golang Avatica 드라이버와도 동작하는 것으로 알려져 있습니다.

테이블 메타데이터는 connection.getMetaData()를 사용하거나 INFORMATION_SCHEMA 테이블을 쿼리해 JDBC로 얻을 수 있어요. 예시는 Get the metadata for a datasource를 참고하세요.

연결 고정 (Connection stickiness)

Druid의 JDBC 서버는 Broker 간에 연결 상태를 공유하지 않습니다. 즉 JDBC를 사용하고 Druid Broker가 여러 개라면, 특정 Broker에 연결하거나 sticky session이 활성화된 로드 밸런서를 사용해야 해요. Druid Router 프로세스는 JDBC 요청을 분산할 때 연결 고정을 제공하며, 일반적인 non-sticky 로드 밸런서를 써도 필요한 고정을 달성하게 해줍니다. 자세한 내용은 Router 문서를 참고하세요.

참고로 non-JDBC JSON over HTTP API는 무상태(stateless)라서 고정이 필요하지 않습니다.

동적 파라미터 (Dynamic parameters)

JDBC 코드에서 이 예시처럼 파라미터화된 쿼리(parameterized query)를 사용할 수 있어요.

PreparedStatement statement = connection.prepareStatement("SELECT COUNT(*) AS cnt FROM druid.foo WHERE dim1 = ? OR dim1 = ?");
statement.setString(1, "abc");
statement.setString(2, "def");
final ResultSet resultSet = statement.executeQuery();

STRING_TO_ARRAY를 사용해 배열을 동적 파라미터로 대체하는 샘플 코드:

PreparedStatement statement = connection.prepareStatement("select l1 from numfoo where SCALAR_IN_ARRAY(l1, STRING_TO_ARRAY(CAST(? as varchar),','))");
List<Integer> li = ImmutableList.of(0, 7);
String sqlArg = Joiner.on(",").join(li);
statement.setString(1, sqlArg);
statement.executeQuery();

네이티브 배열을 사용하는 샘플 코드:

PreparedStatement statement = connection.prepareStatement("select l1 from numfoo where SCALAR_IN_ARRAY(l1, ?)");
Iterable<Object> list = ImmutableList.of(0, 7);
ArrayFactoryImpl arrayFactoryImpl = new ArrayFactoryImpl(TimeZone.getDefault());
AvaticaType type = ColumnMetaData.scalar(Types.INTEGER, SqlType.INTEGER.name(), Rep.INTEGER);
Array array = arrayFactoryImpl.createArray(type, list);
statement.setArray(1, array);
statement.executeQuery();

예제 (Examples)

다음 섹션은 JDBC 커넥터를 사용하는 두 개의 완전한 샘플을 담고 있습니다.

  • Get the metadata for a datasource — INFORMATION_SCHEMA를 쿼리해 컬럼 이름 같은 메타데이터를 얻는 방법을 보여줘요.
  • Query data — 데이터소스에 대해 select 쿼리를 실행합니다.

이 예제들은 사전 조건(prerequisites)을 충족한 뒤 시도해 볼 수 있어요.

연결 옵션에 대한 자세한 내용은 Client Reference를 참고하세요.

사전 조건 (Prerequisites)

이 예제를 시도하기 전에 다음 요구 사항을 충족하는지 확인하세요.

  • 지원되는 Java 버전
  • Avatica JDBC 드라이버. JAR을 CLASSPATH에 직접 추가하거나 Maven 및 pom.xml 파일을 통해 외부에서 관리할 수 있어요.
  • 사용 가능한 Druid 인스턴스. Quickstart (local)에 설명된 micro-quickstart 구성을 사용할 수 있습니다. 예제는 quickstart를 가정하므로, 명시적으로 언급되지 않는 한 인증/권한 부여는 예상하지 않아요.
  • quickstart의 예제 wikipedia 데이터소스가 Druid 인스턴스에 로드되어 있어야 합니다. 다른 데이터소스가 로드되어 있어도 예제를 시도할 수 있지만, 테이블 이름과 컬럼 이름을 자신의 데이터소스에 맞게 수정해야 해요.

데이터소스의 메타데이터 가져오기

컬럼 이름 같은 메타데이터는 INFORMATION_SCHEMA 테이블을 통하거나 connection.getMetaData()를 통해 얻을 수 있어요. 다음 예제는 INFORMATION_SCHEMA 테이블을 사용해 이전 튜토리얼에서 로드한 wikipedia 데이터소스의 컬럼 이름 목록을 가져와 출력합니다.

import java.sql.*;
import java.util.Properties;

public class JdbcListColumns {

    public static void main(String[] args)
    {
        // Connect to /druid/v2/sql/avatica/ on your Router. 
        // You can connect to a Broker but must configure connection stickiness if you do. 
        String url = "jdbc:avatica:remote:url=http://localhost:8888/druid/v2/sql/avatica/;transparent_reconnection=true";

        String query = "SELECT COLUMN_NAME,* FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = 'wikipedia' and TABLE_SCHEMA='druid'";

        // Set any connection context parameters you need here.
        // Any property from https://druid.apache.org/docs/latest/querying/sql-query-context.html can go here.
        Properties connectionProperties = new Properties();

        try (Connection connection = DriverManager.getConnection(url, connectionProperties)) {
            try (
                    final Statement statement = connection.createStatement();
                    final ResultSet rs = statement.executeQuery(query)
            ) {
                while (rs.next()) {
                    String columnName = rs.getString("COLUMN_NAME");
                    System.out.println(columnName);
                }
            }
        } catch (SQLException e) {
            throw new RuntimeException(e);
        }

    }
}

데이터 쿼리하기

이제 어떤 컬럼이 있는지 알았으니 데이터를 쿼리하기 시작할 수 있어요. 다음 예제는 Japan의 타임스탬프와 댓글을 위해 wikipedia라는 이름의 데이터소스를 쿼리합니다. 그리고 쿼리 컨텍스트 파라미터 sqlTimeZone도 설정해요. 선택적으로 동적 파라미터로 쿼리를 파라미터화할 수도 있습니다.

import java.sql.*;
import java.util.Properties;

public class JdbcCountryAndTime {

    public static void main(String[] args)
    {
        // Connect to /druid/v2/sql/avatica/ on your Router. 
        // You can connect to a Broker but must configure connection stickiness if you do. 
        String url = "jdbc:avatica:remote:url=http://localhost:8888/druid/v2/sql/avatica/;transparent_reconnection=true";

        //The query you want to run.
        String query = "SELECT __time, isRobot, countryName, comment FROM wikipedia WHERE countryName='Japan'";

        // Set any connection context parameters you need here.
        // Any property from https://druid.apache.org/docs/latest/querying/sql-query-context.html can go here.
        Properties connectionProperties = new Properties();
        connectionProperties.setProperty("sqlTimeZone", "America/Los_Angeles");

        try (Connection connection = DriverManager.getConnection(url, connectionProperties)) {
            try (
                    final Statement statement = connection.createStatement();
                    final ResultSet rs = statement.executeQuery(query)
            ) {
                while (rs.next()) {
                    Timestamp timeStamp = rs.getTimestamp("__time");
                    String comment = rs.getString("comment");
                    System.out.println(timeStamp);
                    System.out.println(comment);
                }
            }
        } catch (SQLException e) {
            throw new RuntimeException(e);
        }

    }
}

더 알아보기 (Learn more)

  • 연결 옵션 전체 목록은 Client Reference 문서를 참고하세요.
  • Druid SQL 언어에 대한 전반적인 설명은 Druid SQL 문서를 확인해 보세요.