이 글에서 얻는 것#
- 파티셔닝으로 단일 테이블의 대용량 데이터를 분할합니다.
- 샤딩으로 여러 데이터베이스에 데이터를 분산합니다.
- 적절한 분할 전략을 선택할 수 있습니다.
- 대용량 데이터의 성능을 최적화할 수 있습니다.
1) 파티셔닝 (Partitioning)#
1-1) 파티셔닝이란?#
하나의 큰 테이블을 여러 파티션으로 분할
테이블: orders (1억 건)
↓ 파티셔닝
파티션 1: 2023년 주문 (1000만 건)
파티션 2: 2024년 주문 (3000만 건)
파티션 3: 2025년 주문 (6000만 건)
장점:
- 쿼리 성능 향상 (필요한 파티션만 스캔)
- 관리 용이 (오래된 파티션 삭제)
1-2) Range 파티셔닝#
CREATE TABLE orders (
id BIGINT AUTO_INCREMENT,
user_id BIGINT NOT NULL,
amount DECIMAL(10, 2),
created_at DATETIME NOT NULL,
PRIMARY KEY (id, created_at)
) PARTITION BY RANGE (YEAR(created_at)) (
PARTITION p2023 VALUES LESS THAN (2024),
PARTITION p2024 VALUES LESS THAN (2025),
PARTITION p2025 VALUES LESS THAN (2026),
PARTITION p_future VALUES LESS THAN MAXVALUE
);
-- 쿼리 시 자동으로 적절한 파티션만 스캔
SELECT * FROM orders
WHERE created_at >= '2025-01-01' AND created_at < '2025-12-31';
-- p2025 파티션만 스캔!
-- 파티션 추가
ALTER TABLE orders ADD PARTITION (
PARTITION p2026 VALUES LESS THAN (2027)
);
-- 오래된 파티션 삭제
ALTER TABLE orders DROP PARTITION p2023;
1-3) Hash 파티셔닝#
CREATE TABLE users (
id BIGINT AUTO_INCREMENT,
name VARCHAR(100),
email VARCHAR(255),
created_at DATETIME,
PRIMARY KEY (id)
) PARTITION BY HASH(id) PARTITIONS 10;
-- ID를 해시해서 0~9 파티션에 균등 분배
1-4) List 파티셔닝#
CREATE TABLE orders (
id BIGINT,
country VARCHAR(2),
amount DECIMAL(10, 2),
PRIMARY KEY (id, country)
) PARTITION BY LIST COLUMNS(country) (
PARTITION p_asia VALUES IN ('KR', 'JP', 'CN'),
PARTITION p_europe VALUES IN ('UK', 'DE', 'FR'),
PARTITION p_america VALUES IN ('US', 'CA', 'BR')
);
2) 샤딩 (Sharding)#
2-1) 샤딩이란?#
여러 데이터베이스 서버에 데이터 분산
DB 1: user_id 1~1000만
DB 2: user_id 1000만~2000만
DB 3: user_id 2000만~3000만
장점:
- 수평 확장 (서버 추가로 용량 증가)
- 부하 분산
- 장애 격리
2-2) Hash 기반 샤딩#
@Service
public class ShardingService {
private List<DataSource> shards;
public DataSource getShard(Long userId) {
int shardIndex = (int) (userId % shards.size());
return shards.get(shardIndex);
}
public User findUser(Long userId) {
DataSource shard = getShard(userId);
// shard에서 조회
return jdbcTemplate.queryForObject(
"SELECT * FROM users WHERE id = ?",
new Object[]{userId},
new UserRowMapper()
);
}
}
2-3) Range 기반 샤딩#
@Service
public class RangeShardingService {
public DataSource getShard(Long userId) {
if (userId < 10_000_000) {
return shard1;
} else if (userId < 20_000_000) {
return shard2;
} else {
return shard3;
}
}
}
2-4) ShardingSphere 사용#
# application.yml
spring:
shardingsphere:
datasource:
names: ds0,ds1,ds2
ds0:
type: com.zaxxer.hikari.HikariDataSource
driver-class-name: com.mysql.cj.jdbc.Driver
jdbc-url: jdbc:mysql://db1:3306/shard0
ds1:
jdbc-url: jdbc:mysql://db2:3306/shard1
ds2:
jdbc-url: jdbc:mysql://db3:3306/shard2
rules:
sharding:
tables:
users:
actual-data-nodes: ds$->{0..2}.users
database-strategy:
standard:
sharding-column: user_id
sharding-algorithm-name: user-hash
sharding-algorithms:
user-hash:
type: HASH_MOD
props:
sharding-count: 3
3) 주의사항#
⚠️ 샤딩의 문제점#
1. JOIN 어려움:
- 다른 샤드에 있는 테이블 JOIN 불가
- 애플리케이션 레벨에서 처리 필요
2. 트랜잭션:
- 여러 샤드에 걸친 트랜잭션 어려움
- 분산 트랜잭션 또는 보상 트랜잭션 필요
3. 리샤딩 (Re-sharding):
- 샤드 개수 변경 시 데이터 재분배 필요
- 다운타임 발생 가능
- 파티셔닝: 단일 DB 내 테이블 분할
- 샤딩: 여러 DB에 데이터 분산
- 파티셔닝이 샤딩보다 구현 간단
- 대용량 데이터 처리에 필수
다음 단계#
- DB 레플리케이션:
/learning/deep-dive/deep-dive-db-replication/ - 읽기 전용 복제본:
/learning/deep-dive/deep-dive-read-replicas/
💬 댓글