Spring boot 3, AWS Redshift + JPA + Tsid

Spring boot에서 jpa로 redshift를 쓰고 싶다면..
김주혁's avatar
Jul 04, 2024
Spring boot 3, AWS Redshift + JPA + Tsid
 
우리 회사는 로그를 redshift에 쌓고 있는데, 기존에는 myBatis를 사용하다가 다른 프로젝트와 버전을 맞추고 통일성있게 프로젝트 구성을 가져가기 위해 jpa로 마이그레이션 하게 됐다.
 
redshift는 기본적으로 PostgreSQL 기반으로 제작됐지만, PostgreSQL과 다르게 동작하는 특성도 있고 일반 MySQL처럼 돌아가는 부분도 있다.
 
일반적으로 AWS Glue ETL잡과 함께 사용하는 편이고, 대용량 데이터 세트를 처리하기 위해 많이 사용한다. 그리고 데이터를 압축 하여 제공해주는 기능도 있어서 대규모 처리를 위해 충분히 고려할만한 스택이다.
 
이미 이걸 찾아보고 있다는 것은 어딘가의 프로젝트에 도입하기로 결정했다는 것이고, 장단점은 이미 찾아봤을 것이기 때문에 프로젝트 적용을 위한 Redshift의 설명은 여기까지하고, project 설정에 대한 얘기만 하겠다.
 
사용중인 라이브러리 버전
plugins { id 'java' id 'org.springframework.boot' version '3.0.6' id 'io.spring.dependency-management' version '1.1.0' id 'org.asciidoctor.convert' version '1.5.8' } java { sourceCompatibility = '17' } dependencies { runtimeOnly 'com.mysql:mysql-connector-j' implementation 'com.zaxxer:HikariCP:5.0.1' implementation 'org.hibernate.orm:hibernate-core:6.0.2.Final' implementation group: 'org.javassist', name: 'javassist', version: '3.15.0-GA' implementation 'org.apache.commons:commons-pool2:2.11.1' implementation 'com.amazon.redshift:redshift-jdbc42:2.1.0.8' implementation 'org.springframework.boot:spring-boot-starter-data-jpa' implementation 'com.querydsl:querydsl-jpa:5.0.0:jakarta' annotationProcessor "com.querydsl:querydsl-apt:5.0.0:jakarta" annotationProcessor "jakarta.annotation:jakarta.annotation-api" annotationProcessor "jakarta.persistence:jakarta.persistence-api" implementation 'io.hypersistence:hypersistence-utils-hibernate-63:3.7.0' }
 
  1. aws redshift 라이브러리 설치
    1. implementation 'com.amazon.redshift:redshift-jdbc42:2.1.0.8' implementation 'org.springframework.boot:spring-boot-starter-data-jpa' implementation 'com.querydsl:querydsl-jpa:5.0.0:jakarta' annotationProcessor "com.querydsl:querydsl-apt:5.0.0:jakarta" annotationProcessor "jakarta.annotation:jakarta.annotation-api" annotationProcessor "jakarta.persistence:jakarta.persistence-api"
      일반적인 jpa + queryDSL 설정을 포함하고 있다.
       
      여기에 추가적으로 차후에 설명할 id 관련 작업을 위해
      implementation 'io.hypersistence:hypersistence-utils-hibernate-63:3.7.0'
      를 같이 설치해주면 좋다.
       
  1. application.yaml
    1. datasource: driver-class-name: com.amazon.redshift.Driver url: jdbc:redshift://localhost:5439/dbname username: ${DB_USERNAME} password: ${DB_PASSWORD} hikari: max-lifetime: 170000 connection-timeout: 8000 jpa: properties: hibernate: temp: use_jdbc_metadata_defaults: false format_sql: true implicit_naming_strategy: org.springframework.boot.orm.jpa.hibernate.SpringImplicitNamingStrategy physical_naming_strategy: org.hibernate.boot.model.naming.CamelCaseToUnderscoresNamingStrategy hbm2ddl.auto: none jdbc: lob: non_contextual_creation=true: true batch_size: 10 dialect: org.hibernate.dialect.MySQLDialect hibernate: ddl-auto: none open-in-view: false show-sql: false
      세세한 설정은 집어치우고, 여기서 중요하게 봐야할 것은 dialect와 use_jdbc_metadata_defaults다.
      위에서 Redshift가 PostgreSQL 기반이라고 설명했었는데, dialect는 MySQLDialect를 사용했다.
       
      MySQLDialect를 사용하면,
      method com.amazon.redshift.jdbc.redshiftconnectionimpl.createclob() is not yet implemented.
      에러가 발생하게 되는데, 해당 에러는 아직 AWS Redshift JDBC 드라이버가 아직 구현하지 못해서 발생하는 것으로 추정된다.
       
      따라서, 해당 옵션을 끄기 위해
      temp: use_jdbc_metadata_defaults: false
      를 추가해야 정상적으로 돌아간다.
 
  1. 그 외적으로는 일반적인 db랑 모두 똑같으나 redshift에는 치명적인 문제가 있으니 ,바로 Auto generate Identity Column을 JPA에서 사용할 수 없다는 것이다. 처음엔 이 걸 모르고,
    1. @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id;
       
      위와 같이 작성했었는데, 오만가지 에러가 다 발생했다.
       
      수 많은 시행착오가 있었지만 내린 결론은, redshift를 제거할 수 없고 계속 사용해야 하며 jpa까지 포기할 수 없다면 id generate를 포기하자는 것이 결론이었다.
       
      제일먼저 떠오른 것은 가상설계 면접…. 책에서 나오는 snowflake와 uild였으나 두 방식을 합친 tsid라는 방식이 있어, 찾아보고 tsid를 사용하기로 했다.
       
      사용 한 라이브러리는 위에서 기본 설정외에 추가로 설치한
      implementation 'io.hypersistence:hypersistence-utils-hibernate-63:3.7.0'
      였으며, redshift id 타입은 bigint로 주었다.
       
      실 사용 방법은,
      @Id @Tsid private Long id;
      이렇게 사용하면 repository.save(entity)를 실행시켰을 때 자연스럽게 데이터베이스에 Tsid가 들어가게 된다.
 
위와 같이 했을 때 JPA와 Redshift가 연결되어 프로젝트에서 사용할 수 있는 상태가 된다.
 
Redshift의
id integer default "identity"(292331, 0, '1,1'::text) not null encode az64 distkey
이런식으로 생성되는 id 컬럼을 사용하지 못한다는 것은 문제가 있으나, 해당 id 컬럼이 테이블이 생성된 뒤에는 넣을 수 없고… 또 수정도안되며(물론 수정할 일이 생기면 안되겠지만) 해당 id 컬럼이 있는 경우 gui에서 추가할 수도 없는 등의 다양한 문제를 생각하면
 
snowflake나 tsid 혹은 ulid를 사용하는 것도 나쁘지 않은 선택지라고 생각한다.
Share article
RSSPowered by inblog