일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | 6 | 7 |
8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 | 16 | 17 | 18 | 19 | 20 | 21 |
22 | 23 | 24 | 25 | 26 | 27 | 28 |
29 | 30 | 31 |
Tags
- SQL 튜닝
- 비교 연산자
- 상속
- 반복문
- 배열
- 스프링시큐리티 로그아웃
- SpringSecurity 로그인
- 이클립스 설치
- 논리 연산자
- 오버로딩
- 객체
- 연산자
- 객체지향
- 산술 연산자
- StringBuffer
- @PreAuthorize("isAuthenticated()")
- 인텔리제이 Web 애플리케이션
- SpringSecurity 로그아웃
- SQL튜닝
- 자바의정석
- join
- spring 게시판 삭제
- SQL
- 친절한 SQL
- 식별자
- java
- 함수
- 오버라이딩
- 친절한 SQL 튜닝
- 예약어
Archives
- Today
- Total
gi_dor
[MySQL] RDS DB 이중화 읽기전용 + CQRS 본문
CQRS 패턴에 대해 알게되었다
CQRS는 데이터 저장소에 대한 읽기와 업데이트(쓰기) 작업을 구분하는 패턴이라고 한다
CQRS는 명령모델과 쿼리모델로 나뉜다
1. 명령모델 Command Model
- 데이터를 변경하는 작업 (UPDATE , INSERT, DELETE) 를 처리한다
- 특정 명령을 받아서 비즈니스 로직과 유효성 검사를 수행하고 데이터를 변경한다
2. 쿼리모델 Query Model
- 데이터를 조회하는 작업 ( SELECT )을 처리한다
- 최적화된 일기작업을 위해 데이터를 별도의 형태로 저장하기도 한다
명령모델은 주로 서비스로직과 관련이있고 데이터 생성 , 수정 , 삭제 같은 작업을 담당한다
쿼리모델은 데이터베이스나 다른 저장소에서 데이터를 읽고 결과를 반환하는 역할을 담당한다
DB 이중화 ( 정보처리기사 )
- 데이터베이스 이중화(Database Replication)
- 데이터베이스에 문제 발생 시 백업으로서의 역할을 할수도 있다
- 데이터베이스로 분산시켜 처리하므로 쿼리 요청을 분산해 데이터베이스의 부하를 줄일 수 있다ㅏ.
앞에 RDS 만드는 글을 보셨으면 복제본 생성은 크게 어려울것 없습니다
SpringBoot 프로젝트에 DataSourceConfig, RoutingDataSource 클래스 작성
package com.example.bookhub.config;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.jdbc.DataSourceBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.jdbc.datasource.LazyConnectionDataSourceProxy;
import javax.sql.DataSource;
import java.util.HashMap;
@Slf4j
@Configuration
public class DataSourceConfiguration {
private static final String MASTER_SERVER = "MASTER"; // 마스터 서버를 나타내는 상수 문자열입니다.
private static final String REPLICA_SERVER = "REPLICA"; // 복제 서버를 나타내는 상수 문자열입니다.
@Bean
@Qualifier(MASTER_SERVER)
@ConfigurationProperties(prefix = "spring.datasource.master")
public DataSource masterDataSource() {
return DataSourceBuilder.create() // 'spring.datasource.master'로 시작하는 구성 속성을 사용하여 DataSource를 작성합니다.
.build();
}
@Bean
@Qualifier(REPLICA_SERVER)
@ConfigurationProperties(prefix = "spring.datasource.replica")
public DataSource replicaDataSource() {
return DataSourceBuilder.create() // 'spring.datasource.replica'로 시작하는 구성 속성을 사용하여 DataSource를 작성합니다.
.build();
}
@Bean
public DataSource routingDataSource(
@Qualifier(MASTER_SERVER) DataSource masterDataSource,
@Qualifier(REPLICA_SERVER) DataSource replicaDataSource
) {
RoutingDataSource routingDataSource = new RoutingDataSource(); // 사용자 정의 RoutingDataSource 인스턴스를 생성합니다.
HashMap<Object, Object> dataSourceMap = new HashMap<>(); // 데이터 소스를 보관할 맵을 생성합니다.
dataSourceMap.put("master", masterDataSource); // "master" 키로 마스터 DataSource를 맵에 넣습니다.
dataSourceMap.put("replica", replicaDataSource); // "replica" 키로 복제 DataSource를 맵에 넣습니다.
routingDataSource.setTargetDataSources(dataSourceMap); // 라우팅을 위한 대상 데이터 소스를 설정합니다.
routingDataSource.setDefaultTargetDataSource(masterDataSource); // 기본 대상 데이터 소스를 마스터 DataSource로 설정합니다.
return routingDataSource; // routingDataSource를 반환합니다.
}
@Bean
@Primary
public DataSource dataSource() {
DataSource determinedDataSource = routingDataSource(masterDataSource(), replicaDataSource()); // 라우팅 로직을 기반으로 적절한 DataSource를 결정합니다.
return new LazyConnectionDataSourceProxy(determinedDataSource); // 지연 연결 DataSource 프록시를 반환합니다.
}
}
// Properties DB 설정 읽어오기
@ConfigurationProperties(prefix = "spring.datasource.master")
@ConfigurationProperties(prefix = "spring.datasource.replica")
package com.example.bookhub.config;
import jakarta.annotation.Nullable;
import lombok.extern.slf4j.Slf4j;
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
import org.springframework.transaction.support.TransactionSynchronizationManager;
@Slf4j
public class RoutingDataSource extends AbstractRoutingDataSource {
@Nullable
@Override
protected Object determineCurrentLookupKey() {
String lookupKey = TransactionSynchronizationManager.isCurrentTransactionReadOnly() ? "replica" : "master";
log.info("Current DataSource is {}", lookupKey);
return lookupKey;
}
}
isCurrentTransactionReadOnly 이 메서드로 트랜젝션이 readOnly=true일 때는 replica를 반환
실제 replica를 사용 , master를 사용 하는 로직은 AbstractRoutingDataSource에 구현
# master db
spring.datasource.master.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.master.jdbc-url=jdbc:mysql://bookhub.cb044koayrwz.ap-northeast-2.rds.amazonaws.com:3306/bookhub
spring.datasource.master.username=admin
spring.datasource.master.password=ENC(frdBmVOFlCns7XbOH3cDVuObdEx1UE/m)
## read replica
spring.datasource.replica.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.replica.jdbc-url=jdbc:mysql://bookhub-replica.cb044koayrwz.ap-northeast-2.rds.amazonaws.com/bookhub
spring.datasource.replica.username=admin
spring.datasource.replica.password=ENC(HZz6V+9Ztm04oY0SfI53pPzvCjuIwd8B)
Spring Security 를 사용한 로그인 메서드
/**
* 주어진 사용자 아이디를 기준으로 사용자의 데이터를 가져와 UserDetails 객체로 반환합니다.
* @param id 사용자 아이디
* @return UserDetails 객체
* @throws UsernameNotFoundException 주어진 아이디에 해당하는 사용자를 찾을 수 없는 경우 발생합니다.
*/
@Override
@Transactional(readOnly = true)
public UserDetails loadUserByUsername(String id) throws UsernameNotFoundException {
// 사용자 아이디를 기준으로 데이터베이스에서 사용자 정보를 가져옵니다. 이 정보는 user 객체에 저장
User user = userMapper.selectUserById(id);
// 데이터베이스에서 가져온 사용자 정보가 없다면(null이면) 예외를 발생시킵니다.
if(user == null) {
throw new UsernameNotFoundException("등록되지 않은 사용자이거나 탈퇴처리된 사용자입니다. : " +id);
}
if ("Y".equals(user.getDelYn())) {
throw new UsernameNotFoundException("회원탈퇴 처리된 계정입니다 : " +id);
}
// UserDetailsImpl 클래스의 객체를 생성합니다. 이 객체는 사용자의 인증 및 권한 정보를 제공하기위해 사용한다
UserDetailsImpl userDetails = new UserDetailsImpl();
// 객체에서 가져온 사용자 아이디와 비밀번호를 userDetails 객체에 설정
userDetails.setId(user.getId());
userDetails.setPassword(user.getPassword());
userDetails.setAuthorities(List.of(new SimpleGrantedAuthority("ROLE_USER")));
System.out.println("서비스 - 로그인한 아이디 : " +user.getId());
return userDetails;
}
읽기 전용 replica 적용
읽기 적용 하지 않은 메인 페이지 책 목록 가져오는 메서드
EC2 연결 후 배포 에서 확인하기
728x90
'DataBase' 카테고리의 다른 글
[MySQL] 스토리지 엔진 (4) | 2024.08.29 |
---|---|
트랜잭션(Trasaction) 이란 ? (0) | 2024.08.19 |
[MySQL] MySQL 프로파일링 - 인덱스 SELECT 성능 비교 (0) | 2024.05.08 |
SQL ) 테이블 생성시 Deleted 컬럼이 필요 할까? (0) | 2023.12.13 |
DB 기본 (2) | 2023.12.04 |