스프링 부트 애플리케이션 입장에서 샤딩된 DB를 쓰기 위해서 직접 “복잡한” 코드를 많이 짤 필요는 없습니다. 대신, 주로 다음 두 가지 레벨 중 하나의 방식으로 샤딩을 처리하게 됩니다.
1. 외부 프록시/미들웨어 사용 (Mycat, Vitess, ProxySQL 등)
-
애플리케이션 변경 제로 또는 최소화
- 샤드별 DB URL(호스트/포트) 대신, 프록시의 단일 접점(가상 호스트/포트)을
spring.datasource.url에 지정 - 트래픽·샤드 키 기반 분기 로직은 프록시가 대신 수행
- 샤드별 DB URL(호스트/포트) 대신, 프록시의 단일 접점(가상 호스트/포트)을
-
구현 사항
-
application.yml에 한번만 프록시 접점 정보 등록spring: datasource: url: jdbc:mysql://proxy.example.com:3306/app_db username: app password: secret -
필요 시 트랜잭션 격리, 커넥션 풀, 리플리카 읽기 전용 설정 등 추가 구성
-
-
장점
- 코드 변경 거의 없음
- 프록시 단에서 리밸런싱·모니터링·장애 조치 지원
-
단점
- 별도 인프라 관리 필요
- 샤드 키가 직접 애플리케이션에 노출되지 않아 디버깅 어려움
2. 애플리케이션 레벨 내장 샤딩 (예: Apache ShardingSphere-JDBC, Spring AbstractRoutingDataSource)
A. ShardingSphere-JDBC 사용
-
의존성 추가
<dependency> <groupId>org.apache.shardingsphere</groupId> <artifactId>sharding-jdbc-spring-boot-starter</artifactId> <version>5.x.x</version> </dependency> -
샤딩 룰 설정 (
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 -
별도 코드 작성 불필요
- Spring Data JPA / MyBatis 등 기존 리포지토리·매퍼 그대로 사용
@Transactional지원
B. 직접 AbstractRoutingDataSource 구현
-
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; } -
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. 트랜잭션 & 모니터링
-
트랜잭션 매니저
- 여러 DataSource 전환 시
ChainedTransactionManager혹은 XA 트랜잭션 사용 검토
- 여러 DataSource 전환 시
-
메트릭 노출
- 커넥션 풀, 쿼리 지연, 샤드별 QPS 등을 Micrometer → Prometheus↑Grafana로 모니터링
결론
- “아예 구현할 것도 없다”: 외부 샤딩 프록시를 쓰면 Spring Boot 쪽 코드는 거의 바뀌지 않습니다.
- “약간의 설정만”: ShardingSphere-JDBC 정도를 도입하면, 의존성과 YAML 세 줄만 추가하고 기존 코드 그대로 둬도 샤딩이 적용됩니다.
- “직접 짜려면”: AbstractRoutingDataSource + AOP로 커스텀 라우팅 로직을 만들어야 하지만, 보통은 검증된 라이브러리를 권장합니다.