Pink Spider/Boot에서 샤딩된 DB 사용하려면

Created Tue, 10 Jun 2025 08:49:58 +0900 Modified Mon, 08 Dec 2025 08:41:47 +0900
819 Words 4 min

스프링 부트 애플리케이션 입장에서 샤딩된 DB를 쓰기 위해서 직접 “복잡한” 코드를 많이 짤 필요는 없습니다. 대신, 주로 다음 두 가지 레벨 중 하나의 방식으로 샤딩을 처리하게 됩니다.


1. 외부 프록시/미들웨어 사용 (Mycat, Vitess, ProxySQL 등)

  • 애플리케이션 변경 제로 또는 최소화

    • 샤드별 DB URL(호스트/포트) 대신, 프록시의 단일 접점(가상 호스트/포트)을 spring.datasource.url에 지정
    • 트래픽·샤드 키 기반 분기 로직은 프록시가 대신 수행
  • 구현 사항

    1. application.yml에 한번만 프록시 접점 정보 등록

      spring:
        datasource:
          url: jdbc:mysql://proxy.example.com:3306/app_db
          username: app
          password: secret
      
    2. 필요 시 트랜잭션 격리, 커넥션 풀, 리플리카 읽기 전용 설정 등 추가 구성

  • 장점

    • 코드 변경 거의 없음
    • 프록시 단에서 리밸런싱·모니터링·장애 조치 지원
  • 단점

    • 별도 인프라 관리 필요
    • 샤드 키가 직접 애플리케이션에 노출되지 않아 디버깅 어려움

2. 애플리케이션 레벨 내장 샤딩 (예: Apache ShardingSphere-JDBC, Spring AbstractRoutingDataSource)

A. ShardingSphere-JDBC 사용

  1. 의존성 추가

    <dependency>
      <groupId>org.apache.shardingsphere</groupId>
      <artifactId>sharding-jdbc-spring-boot-starter</artifactId>
      <version>5.x.x</version>
    </dependency>
    
  2. 샤딩 룰 설정 (application.yml)

    spring:
      shardingsphere:
        datasource:
          names: ds0, ds1
          ds0:
            url: jdbc:mysql://db0:3306/app
            username: user
            password: pw
          ds1:
            url: jdbc:mysql://db1:3306/app
            username: user
            password: pw
        rules:
          sharding:
            tables:
              orders:
                actual-data-nodes: ds${0..1}.orders_${0..1}
                table-strategy:
                  inline:
                    sharding-column: user_id
                    algorithm-expression: orders_${user_id % 2}
                key-generator:
                  column: order_id
                  type: SNOWFLAKE
    
  3. 별도 코드 작성 불필요

    • Spring Data JPA / MyBatis 등 기존 리포지토리·매퍼 그대로 사용
    • @Transactional 지원

B. 직접 AbstractRoutingDataSource 구현

  1. RoutingDataSource 빈 등록

    @Bean
    public DataSource routingDataSource() {
      Map<Object,Object> target = Map.of(
        "shard0", createDataSource("jdbc:...db0"),
        "shard1", createDataSource("jdbc:...db1")
      );
      var routing = new RoutingDataSource();
      routing.setTargetDataSources(target);
      routing.setDefaultTargetDataSource(target.get("shard0"));
      return routing;
    }
    
  2. ShardKeyHolder + AOP 인터셉터

    public class ShardKeyHolder {
      private static final ThreadLocal<Integer> key = new ThreadLocal<>();
      public static void set(int v) { key.set(v); }
      public static Object get() { return key.get(); }
      public static void clear() { key.remove(); }
    }
    
    public class ShardRoutingInterceptor implements MethodInterceptor {
      public Object invoke(MethodInvocation inv) throws Throwable {
        // 예: 파라미터에서 userId 추출
        int userId = ...;
        ShardKeyHolder.set(userId % 2);
        try {
          return inv.proceed();
        } finally {
          ShardKeyHolder.clear();
        }
      }
    }
    
  3. 결과

    • 비즈니스 로직에서 샤드 로직 완전 제어 가능
    • 다만 구현·테스트·유지보수 부담 증가

3. 트랜잭션 & 모니터링

  • 트랜잭션 매니저

    • 여러 DataSource 전환 시 ChainedTransactionManager 혹은 XA 트랜잭션 사용 검토
  • 메트릭 노출

    • 커넥션 풀, 쿼리 지연, 샤드별 QPS 등을 Micrometer → Prometheus↑Grafana로 모니터링

결론

  • “아예 구현할 것도 없다”: 외부 샤딩 프록시를 쓰면 Spring Boot 쪽 코드는 거의 바뀌지 않습니다.
  • “약간의 설정만”: ShardingSphere-JDBC 정도를 도입하면, 의존성과 YAML 세 줄만 추가하고 기존 코드 그대로 둬도 샤딩이 적용됩니다.
  • “직접 짜려면”: AbstractRoutingDataSource + AOP로 커스텀 라우팅 로직을 만들어야 하지만, 보통은 검증된 라이브러리를 권장합니다.