1) 데이터베이스 다중화
(1) 목적
대부분의 애플리케이션은 읽기 연산의 비중이 쓰기 연산보다 훨씬 높다. 따라서 더 나은 성능을 위해 데이터 변경은 주 데이터베이스에서, 읽기 연산은 서브 데이터베이스로 분산한다
-> 주 데이터베이스 : 쓰기 연산(INSERT, UPDATE, DELETE)/ 서브 데이터베이스 : 주 DB에서 사본 전달받음, 읽기 연산(SELECT)
(2) 스프링에서..
spring:
datasource:
url: jdbc:mysql://127.0.0.1:3306/kotlin_spring?serverTimezone=UTC
driver-class-name: com.mysql.cj.jdbc.Driver
username: root
password: 1234
하나의 데이터소스를 사용할 경우 yaml에 작성해두면 스프링에서는 자동으로 데이터소스를 생성한다
그러나
spring:
datasource:
master:
hikari:
driver-class-name: com.mysql.cj.jdbc.Driver
jdbc-url: jdbc:mysql://127.0.0.1:3306/multiple-datesource?serverTimezone=UTC
read-only: false
username: root
password: 1234
slave:
hikari:
driver-class-name: com.mysql.cj.jdbc.Driver
jdbc-url: jdbc:mysql://127.0.0.1:3306/multiple-datesource?serverTimezone=UTC
read-only: true
username: root
password: 1234
부 데이터베이스(두 개 이상의 데이터소스)를 사용하고 싶을 경우, 코드의 추가 작성이 필요하다
1. 빈의 수동 등록
@Configuration
public class MasterDataSourceConfig {
@Primary
@Bean(name = "masterDataSource")
@ConfigurationProperties(prefix="spring.datasource.master.hikari")
public DataSource masterDataSource() {
return DataSourceBuilder.create()
.type(HikariDataSource.class)
.build();
}
}
@Configuration
public class SlaveDataSourceConfig {
@Bean(name = "slaveDataSource")
@ConfigurationProperties(prefix="spring.datasource.slave.hikari")
public DataSource slaveDataSource() {
return DataSourceBuilder.create()
.type(HikariDataSource.class)
.build();
}
}
2. Read-Only 옵션에 따라 어떤 데이터소스를 사용할지 분기 처리
public class ReplicationRoutingDataSource extends AbstractRoutingDataSource {
@Override
protected Object determineCurrentLookupKey() {
DataSourceType dataSourceType = TransactionSynchronizationManager
.isCurrentTransactionReadOnly() ? DataSourceType.Slave : DataSourceType.Master;
return dataSourceType;
}
}
public enum DataSourceType {
Master, Slave
}
- https://cheese10yun.github.io/spring-transaction/
- http://webcache.googleusercontent.com/search?q=cache:Lw-c3SLW8LEJ:kwon37xi.egloos.com/5364167&cd=11&hl=ko&ct=clnk&gl=kr
cf) AWS Aurota MYSQL과 MariaDB Connector/J를 사용하면 하나의 데이터소스를 마스터로 등록해두면 자동으로 요청 분기가 처리된다(MariaDB Connector/J 최신버전은 지원 X)
+ 레플리카 관련 개념잡기
2) 캐시
캐시란, 값비싼 연산 결과 또는 자주 참조되는 데이터를 메모리 안에 두고 사용하는 임시 저장소
장점 : 직접 조회보다 성능이 좋고 DB 부하를 줄일 수 있다
(1) 로컬 캐시
- 각각의 WAS에 Local Cache가 있는 형태
장점 : 네트워크를 통한 호출이 없고, 서버의 물리 메모리에 직접 접근하기 때문에 빠르다
단점 : 서버가 여러 대인 경우 동기화 문제 발생, 인스턴스 물리 메모리 사이즈 제약이 있다
(2) 글로벌 캐시
- 하나의 캐시 서버를 두고 다수의 WAS가 이용
장점 : 동기화 문제 없음
단점 : 네트워크 호출이 필요하다, 상대적으로 로컬보다 느림, 캐시 서버 장애에 대비해야 한다
Java Application 성능개선에 대해 알아보자 - Local Cache 편
시작하기 앞서... Application을 개발하다 보면 기능이 점점 복잡해지고 데이터가 쌓이면서, 처음과 다른 성능 저하가 발생하게 된다. 서버의 사양을 올리거나, 서버의 댓수를 추가하면 전체적인 성
dev.gmarket.com
3) CDN
정적 콘텐츠를 전송하는 데 쓰이는 분산된 서버
4) 트랜잭션
애플리케이션에서 몇 개의 읽기와 쓰기를 하나의 논리 단위로 묶는 방법
현실에서 시스템은 다음과 같은 상황에 마주할 수 있다
- 쓰기 연산중 실패
- 아무 때나 뻗을 수 있다
- 네트워크 단절
- 여러 클라이언트가 동시에 쓰기 연산을 수행해 데이터를 덮어 씌울 수 있다
- 부분적으로 갱신된 비정상적인 데이터를 읽을 수 있다
- 클라이언트 끼리 경합이 발생해 예상치못한 동작을 할 수 있다
ACID
원자성, 격리성, 지속성은 DB의 속성인 반면, 일관성은 애플리케이션 속성이다
1. 원자성
더 작은 부분으로 쪼갤 수 없음을 의미 ~ 시스템은 연산 전/후의 상태만 있을 수 있다
= Commit/Rollback만 존재한다
2. 일관성
데이터 불변식을 보장하는 것 ~ 데이터 유효성을 판단하는 것은 애플리케이션
데이터베이스는 본질적으로 불변식을 침해하는 데이터를 쓰지 못하게 막을 수 없다( 외래 키를 통한 제약조건 또는 유일성 제약조건을 추가해야한 가능)
3. 격리성
동시에 실행되는 트랜잭션은 서로 격리된다는 의미
앱에서 직렬성 격리를 사용하면 한 번에 하나의 트랜잭션만 발생하는 것처럼 할 수 있다. 그러나 성능이 떨어진다
+ READ UNCOMMITTED, READ COMMITTED, REPEATABLE READ
4. 지속성
트랜잭션이 성공적으로 Commit되면 DB안에 데이터가 영구 저장된다는 의미
+ @Transactional
5) DB Lock
낙관적 락
- 트랜잭션이 Commit될 때, 데이터베이스는 격리가 위반되었는지 체크한다. 위반 시 Rollback
경쟁이 심하지 않은 상황이라면 낙관적 락이 비관적 락보다 성능이 좋은 편이다. 그러나 경쟁이 심해질 경우 성능이 더 떨어질 수 있다
@Entity
@OptimisticLocking(type = OptimisticLockType.VERSION)
public class Product {
@Id
private Long id;
private String name;
@Version
private Long version;
}
비관적 락
- 각 트랜잭션이 실행되는 동안 전체 데이터베이스에 독점 잠금을 획득
= 락이 걸린 상태에서 다른 트랜잭션은 락이 끝날 때까지 대기하는 상태
락 시간을 줄이려면 개별 트랜잭션의 성능을 향상시키는 방법밖에 없다
s Lock : 다른 사용자가 동시에 읽을 수 있지만 Update, Delete를 방지 ~ JPA : PESSIMISTIC.READ
x Lock : 다른 사용자가 읽기, 수정, 삭제 모두 불가능하게 함 ~ JPA : PESSIMISTIC.WRITE
분산 락
- 여러 서버에서 공유된 데이터를 제어하기 위해 사용, Redis를 많이 사용하고 ZooKeeper를 이용해 구현할 수 있다
- Java + Redis : Redisson을 사용해 분산락 구현 가능
- 스핀락, Pub/Sub, While(true) { // setLock } / Redission PUB/SUB
분산락 참고)
https://hyperconnect.github.io/2019/11/15/redis-distributed-lock-1.html
레디스와 분산 락(1/2) - 레디스를 활용한 분산 락과 안전하고 빠른 락의 구현
레디스를 활용한 분산 락에 대해 알아봅니다. 그리고 성능을 높이고 일관성을 보장하는 방법에 대해 알아봅니다.
hyperconnect.github.io
https://helloworld.kurly.com/blog/distributed-redisson-lock/
풀필먼트 입고 서비스팀에서 분산락을 사용하는 방법 - Spring Redisson
어노테이션 기반으로 분산락을 사용하는 방법에 대해 소개합니다.
helloworld.kurly.com
https://channel.io/ko/blog/distributedlock_2022_backend
Distributed Lock 구현 과정
채널톡의 메시징 환경은 대부분 DynamoDB의 Optimistic locking으로도 무리 없는 구조로 구성되어 있지만 외부 메신저의 연동 과정에서 별도 Distributed Lock이 필
channel.io
'개발공부 > 원티드 챌린지 정리' 카테고리의 다른 글
8월 백엔드 챌린지 : 도커 프로 (2) (0) | 2023.10.02 |
---|---|
8월 백엔드 챌린지 : 도커 프로 (1) (0) | 2023.09.21 |
7월 백엔드 챌린지 4. 예제 적용 (0) | 2023.09.18 |
7월 백엔드 챌린지 3. 사용자 수에 따른 규모를 확장하는 방법 2 (0) | 2023.08.06 |
7월 백엔드 챌린지 1. 기술 면접에서 시스템 설계 문제가 가지는 의미 (0) | 2023.07.31 |