-
Notifications
You must be signed in to change notification settings - Fork 0
Open
Labels
documentationImprovements or additions to documentationImprovements or additions to documentation기능 구현기능 구현 입니다.기능 구현 입니다.
Milestone
Description
개요
-회원 탈퇴 시, 탈퇴한 회원의 데이터를 3개월 동안 보관한 후 일괄 삭제하는 방식으로 설계되었습니다. 대량의 데이터를 효율적으로 삭제하기 위해 매일 00:05에 스프링 배치를 활용하여 3개월이 지난 회원의 데이터를 삭제합니다.
구성
- 다중 DB 구성: metaDB와 dataDB로 나누어 설계.
- JPA 기반 배치: 프로젝트는 JPA를 기본으로 사용하므로, JPA를 활용한 배치 설정을 적용.
- JDBC 기반 페이징 처리: 성능 이슈를 최소화하기 위해 배치 실행 시 JDBC 기반의 페이징 방식을 적용.
- MySQL 프로시저 활용: 단일 잡에서 여러 쿼리를 적용하기 어려운 문제를 해결하기 위해, MySQL 프로시저를 사용하여 대량 데이터 삭제를 처리.
- 크론 스케줄링: 매일 00:05에 배치가 자동 실행되도록 크론 스케줄링 설정.
설정
MetaDBConfig
@Configuration
public class MetaDBConfig {
@Primary
@Bean
@ConfigurationProperties(prefix = "spring.datasource-meta")
public DataSource metaDataSource() {
return DataSourceBuilder.create().build();
}
@Primary
@Bean
public PlatformTransactionManager metaTransactionManager() {
return new DataSourceTransactionManager(metaDataSource());
}
}DataDBConfig
@Configuration
@EnableTransactionManagement
@EnableJpaRepositories(
basePackages = {"team9499.commitbody"},
entityManagerFactoryRef = "dataEntityManager",
transactionManagerRef = "dataTransactionManager"
)
public class DataDBConfig {
@Bean
@ConfigurationProperties(prefix = "spring.datasource-data")
public DataSource dataDBSource() {
return DataSourceBuilder.create().build();
}
@Bean
public LocalContainerEntityManagerFactoryBean dataEntityManager() {
LocalContainerEntityManagerFactoryBean em = new LocalContainerEntityManagerFactoryBean();
em.setDataSource(dataDBSource());
em.setPackagesToScan("team9499.commitbody");
em.setJpaVendorAdapter(new HibernateJpaVendorAdapter());
HashMap<String, Object> properties = new HashMap<>();
properties.put("hibernate.hbm2ddl.auto", "update");
properties.put("hibernate.show_sql", "true");
em.setJpaPropertyMap(properties);
return em;
}
@Bean
public PlatformTransactionManager dataTransactionManager() {
JpaTransactionManager transactionManager = new JpaTransactionManager();
transactionManager.setEntityManagerFactory(dataEntityManager().getObject());
return transactionManager;
}
}사용자 탈퇴 배치 전체 코드
@Slf4j
@Configuration
public class MemberDeleteBatch {
private final PlatformTransactionManager dataTransactionManager;
private final JobRepository jobRepository;
private final DataSource dataDBSource;
public MemberDeleteBatch(@Qualifier("dataTransactionManager") PlatformTransactionManager dataTransactionManager,
JobRepository jobRepository,
@Qualifier("dataDBSource") DataSource dataDBSource) {
this.dataTransactionManager = dataTransactionManager;
this.jobRepository = jobRepository;
this.dataDBSource = dataDBSource;
}
@Bean
public Job deleteMemberJob() {
log.info("탈퇴한 사용자 데이터 삭제 배치");
return new JobBuilder("deleteMemberJob", jobRepository)
.start(firstStep())
.build();
}
@Bean
public Step firstStep() {
return new StepBuilder("firstStep", jobRepository)
.<Member, Member> chunk(10, dataTransactionManager)
.reader(beforeReader())
.processor(itemProcessor())
.writer(deleteWriters())
.build();
}
@Bean
public JdbcPagingItemReader<Member> beforeReader(){
return new JdbcPagingItemReaderBuilder<Member>()
.name("beforeReader")
.dataSource(dataDBSource)
.selectClause("select member_id")
.fromClause("from member")
.whereClause("where is_withdrawn = true and withdrawn_at <= date(now()) and withdrawn_at is not null")
.sortKeys(Map.of("member_id", Order.ASCENDING))
.rowMapper(new CustomMemberRowMapper())
.pageSize(10)
.build();
}
@Bean
public ItemProcessor<Member,Member> itemProcessor (){
return member -> {
log.info("탈퇴 사용자 Id ={}",member.getId());
return member;
};
}
@Bean
public JdbcBatchItemWriter<Member> deleteWriters() {
String sql = "CALL delete_member_data(:id)";
return new JdbcBatchItemWriterBuilder<Member>()
.dataSource(dataDBSource)
.sql(sql)
.itemSqlParameterSourceProvider(new BeanPropertyItemSqlParameterSourceProvider<>())
.assertUpdates(false)
.build();
}
}문제점
- 단일 DB 연결 시 .yml 파일에서 .url 설정을 사용했지만, 다중 DB 구성에서는 jdbc-url로 설정을 변경해야 함.
- 테이블 및 컬럼 생성 시 카멜케이스가 적용되지 않아, @column(name = "")을 통해 정확한 컬럼명을 명시해야 함.
- 두 개의 트랜잭션을 사용하는 상황에서, metaDB에 적용된 트랜잭션을 명시하기 위해 @transactional(transactionManager = "트랜잭션명")을 사용해야 함
Reactions are currently unavailable
Metadata
Metadata
Assignees
Labels
documentationImprovements or additions to documentationImprovements or additions to documentation기능 구현기능 구현 입니다.기능 구현 입니다.
Type
Projects
Status
진행중