1. ๊ฐ์
์ด์ ํ๋ก์ ํธ์์ ์ผ๊ด์ ์ผ๋ก ์๋ฆผ์ ๋ณด๋ด๋ ๊ธฐ๋ฅ์ ๊ฐ๋ฐํ์๋๋ฐ ๊ธฐ๋ฅ ๊ตฌํ์ ์ํด ์คํ๋ง ๋ฐฐ์น๋ฅผ ํ์ตํ์๋ค. ๊ทธ๋๋ ItemReader์ ์ข ๋ฅ๊ฐ ์ฌ๋ฌ๊ฐ์ง ์๋ ๊ฑด ์์์ง๋ง ์ฐ์ ์๋ฌด๊ฑฐ๋ ์ ํํด ๊ตฌํํ์๋๋ฐ ์ด๋ฒ ๊ธฐํ์ ์ ๋๋ก ์์๋ณด๋ ค๊ณ ํ๋ค.
2. Spring Batch Chunk?
Spring Batch์์ ์ผ๊ด ์ฒ๋ฆฌ๋ฅผ ํ๋ ๋ฐฉ๋ฒ์ ํฌ๊ฒ ๋๊ฐ์ง๊ฐ ์๋ค. ๋ฐ๋ก Tasklet ๊ธฐ๋ฐ๊ณผ Chunk ๊ธฐ๋ฐ์ธ๋ฐ ItemReader์ Chunk ๋ฐฉ์์์ ์ฌ์ฉ๋๋ ๊ฒ์ด๋ค. ItemReader์ ํ์ตํ๊ธฐ ์์ Chunk์ ๋ํด ๊ฐ๋ณ๊ฒ ์์๋ณด์.
Spring Batch์์์ Chunk๋ ๋ฐ์ดํฐ ๋ฉ์ด๋ฆฌ๋ก ์์ ํ ๋ ๊ฐ ์ปค๋ฐ ์ฌ์ด์ ์ฒ๋ฆฌ๋๋ row ์๋ฅผ ์๊ธฐํ๋ค.
์ฆ, Chunk ์งํฅ ์ฒ๋ฆฌ๋ ํ ๋ฒ์ ํ๋์ฉ ๋ฐ์ดํฐ๋ฅผ ์ฝ์ด Chunk๋ผ๋ ๋ฉ์ด๋ฆฌ๋ฅผ ๋ง๋ ๋ค, Chunk ๋จ์๋ก ํธ๋์ญ์ ์ ๋ค๋ฃจ๋ ๊ฒ์ ์๋ฏธํ๋ค.
์ฌ๊ธฐ์ ํธ๋์ญ์ ์ด๋ผ๋๊ฒ ์ค์ํ๋ฐ, Chunk ๋จ์๋ก ํธ๋์ญ์ ์ ์ํํ๊ธฐ ๋๋ฌธ์ ์คํจํ ๊ฒฝ์ฐ์ ํด๋น Chunk ๋งํผ๋ง ๋กค๋ฐฑ์ด ๋๊ณ , ์ด์ ์ ์ปค๋ฐ๋ ํธ๋์ญ์ ๋ฒ์๊น์ง๋ ๋ฐ์์ด ๋๋ค.
Chunk ์งํฅ ์ฒ๋ฆฌ๊ฐ ๊ฒฐ๊ตญ Chunk ๋จ์๋ก ๋ฐ์ดํฐ๋ฅผ ์ฒ๋ฆฌํ๋ค๋ ์๋ฏธ์ด๊ธฐ ๋๋ฌธ์ ๊ทธ๋ฆผ์ผ๋ก ํํํ๋ฉด ์๋์ ๊ฐ๋ค.
์ ์์ ์์ read() ์์ ์ ์ํํ๋ ๊ฒ ItemReader ์ด๋ค.
Spring Batch์ ItemReader๋ ๊ผญ DB์ ๋ฐ์ดํฐ๋ง์ด ์๋ File, XML, Json๋ฑ ๋ค๋ฅธ ๋ฐ์ดํฐ ์์ค๋ฅผ ๋ฐฐ์น ์ฒ๋ฆฌ์ ์ ๋ ฅ์ผ๋ก๋ ์ฌ์ฉํ ์ ์๋ค.
3. Spring Batch์ ๋ํ์ ์ธ Reader
Spring Batch๋ ๋ํ์ ์ผ๋ก 2๊ฐ์ Reader ํ์ ์ ์ง์ํ๋ค.
Cursor ํ์
Database์ ์ปค๋ฅ์ ์ ๋งบ์ ํ ๋ฐ์ดํฐ๋ฅผ Streaming ํด์ ๋ณด๋ธ๋ค. ๊ทธ๋ฆฌ๊ณ Cursor๋ฅผ ํ ์นธ์ฉ ์ฎ๊ธฐ๋ฉด์ ๋ฐ์ดํฐ๋ฅผ ๊ฐ์ ธ์จ๋ค.
ํ๋์ Connection์ผ๋ก Batch๊ฐ ๋๋ ๋๊น์ง ์ฌ์ฉ๋๊ธฐ ๋๋ฌธ์ Batch๊ฐ ๋๋๊ธฐ ์ ์ Database์ ์ ํ๋ฆฌ์ผ์ด์ ์ Connection์ด ๋จผ์ ๋์ด์ง ์ ์์ด์ ์ํํ๋ค. ๋ชจ๋ ๋ฐ์ดํฐ๋ฅผ ๋ฉ๋ชจ๋ฆฌ์ ์ ์ฅํ๊ธฐ ๋๋ฌธ์ ๋ฉ๋ชจ๋ฆฌ ์ฌ์ฉ๋์ด ๋ง์ต๋๋ค.
Paging
๋ฐ๋ฉด Paging์ ์ข ๋ ๋ง์ ์์ ์ด ํ์๋ก ํ๋ค.
Paging ๊ฐ๋ ์ ํ์ด์ง๋ผ๋ Chunk๋ก Database์์ ๋ฐ์ดํฐ๋ฅผ ๊ฒ์ํ๋ค. ์ฆ, ํ์ด์ง ๋จ์๋ก ๋ฐ์ดํฐ๋ฅผ ํ ๋ฒ์ ์กฐํํด์ค๋ ๋ฐฉ์์ธ๋ฐ, ํ์ด์ง ๋จ์๋ก DB Connection์ ๋งบ๊ธฐ ๋๋ฌธ์ ์ฐ๊ฒฐ ์๊ฐ์ด ์๋์ ์ผ๋ก ์ ์ ํธ์ด๋ค. ๋ํ ํ์ด์ง ๋จ์์ ๊ฒฐ๊ณผ๋ง ๋ฉ๋ชจ๋ฆฌ์ ์ ์ฅํ๊ธฐ ๋๋ฌธ์ ์๋์ ์ผ๋ก ๋ฉ๋ชจ๋ฆฌ ์ฌ์ฉ๋์ด ์ ๋ค.
์ฃผ์์ฌํญ์ผ๋ก Paging์ ์ฌ์ฉํ ๊ฒ์ด๋ผ๋ฉด ๋ฐ์ดํฐ๊ฐ ์ ๋ ฌ์ด ๋์ด์์ด์ผ ํ๋ค.
๊ฒ์ํ์ ํ์ด์ง์ ๊ตฌํํด๋ณด๋ฉด ์๊ฒ ์ง๋ง ํ์ด์ง์ ํ๋ค๋ ๊ฒ์ ๊ฐ ์ฟผ๋ฆฌ์ ์์ ํ ๋ฒํธ (offset) ์ ํ์ด์ง์์ ๋ฐํ ํ ํ ์ (limit)๋ฅผ ์ง์ ํด์ผํ๋ค๋ ๊ฒ์ ์๋ฏธํ๋ค. Spring Batch์์๋ offset๊ณผ limit์ PageSize์ ๋ง๊ฒ ์๋์ผ๋ก ์์ฑํด ์ค๋ค.
๋ค๋ง ๊ฐ ์ฟผ๋ฆฌ๋ ๊ฐ๋ณ์ ์ผ๋ก ์คํํ๋ค๋ ์ ์ ์ ์ํด์ผํ๋ค. ๊ฐ ํ์ด์ง๋ง๋ค ์๋ก์ด ์ฟผ๋ฆฌ๋ฅผ ์คํํ๋ฏ๋ก ํ์ด์ง์ ๊ฒฐ๊ณผ๋ฅผ ์ ๋ ฌํ๋ ๊ฒ์ด ์ค์ํ๋ค.
Cursor, Paging ๊ธฐ๋ฐ์ ๋์ ๋ฐฉ์
4. JdbcPagingItemReader
JdbcPagingItemRedaer๋ JdbcCursorItemReader์ ๊ฐ์ JdbcTemplate ์ธํฐํ์ด์ค๋ฅผ ์ด์ฉํ PagingItemReader์ด๋ค. ์์ ์ฝ๋๋ ์๋์ ๊ฐ๋ค.
// Cursor ๊ธฐ๋ฐ ItemReader ๊ตฌํ์ฒด
@Bean
public JdbcCursorItemReader<Schedule> allReadReader() {
return new JdbcCursorItemReaderBuilder<Schedule>()
.verifyCursorPosition(false)
.fetchSize(FETCH_SIZE)
.dataSource(dataSource)
.rowMapper(new BeanPropertyRowMapper<>(Schedule.class))
.sql("select * from schedules order by id")
.name("jdbcCursorItemReader")
.build();
}
// Paging ๊ธฐ๋ฐ ItemReader ๊ตฌํ์ฒด
@Bean
public JdbcPagingItemReader<Schedule> allReadPagingReader(
PagingQueryProvider queryProvider) {
return new JdbcPagingItemReaderBuilder<Schedule>()
.pageSize(CHUNK_SIZE)
.fetchSize(FETCH_SIZE)
.dataSource(dataSource)
.rowMapper(new BeanPropertyRowMapper<>(Schedule.class))
.queryProvider(queryProvider)
.name("jdbcCursorItemReader")
.build();
}
@Bean
public PagingQueryProvider queryProvider() throws Exception {
SqlPagingQueryProviderFactoryBean queryProvider = new SqlPagingQueryProviderFactoryBean();
queryProvider.setDataSource(dataSource);
queryProvider.setSelectClause("*");
queryProvider.setFromClause("from schedules");
Map<String, Order> sortKeys = new HashMap<>(1);
sortKeys.put("id", Order.ASCENDING);
queryProvider.setSortKeys(sortKeys);
return queryProvider.getObject();
}
JdbcPagingItemReader ๊ณผ JdbcCursorItemReader ์ ๊ฐ์ฅ ํฐ ์ฐจ์ด์ ์ ์ฟผ๋ฆฌ๋ฅผ ๋ ๋ฆฌ๋ ๋ฐฉ์์ด๋ค. Paging ๋ฐฉ์์์ queryProvider ๋ฅผ ์ฌ์ฉํ์ฌ ์ฟผ๋ฆฌ๋ฅผ ์์ฑํ๋ค.
์ด๋ ๊ฐ DB์์ Paging์ ์ง์ํ๋ ์์ฒด์ ์ธ ์ ๋ต๋ค์ด ๋ค๋ฅด๊ธฐ ๋๋ฌธ์ด๋ค.
๊ฐ DB์ Paging ์ ๋ต์ ๋ง์ถ Provider๋ ์๋์ ๊ฐ๋ค.
Spring batch์์ SqlPagingQueryProviderFactoryBean์ ํตํด Datasource ์ค์ ๊ฐ์ ๋ณด๊ณ ์ ์ด๋ฏธ์ง์์ ์์ฑ๋ Provider์ค ํ๋๋ฅผ ์๋์ผ๋ก ์ ํํ๋ค.
5. JpaPagingItemReader
๊ฒฐ๊ณผ์ ์ผ๋ก ๋ด ์ ํ์ JpapagingItemReader ์ด๋ค.
AWS EC2์ ๋ฐฐํฌํด๋๊ณ ์์ ๋ฉ๋ชจ๋ฆฌ ์คํ์ ๊ฐ์ง ํ๊ฒฝ์ ์ ์๋ฒ๊ฐ ๋๊ณ ์์๊ธฐ ๋๋ฌธ์ ๋์ฉ๋ ๋ฐ์ดํฐ๋ฅผ ์ฒ๋ฆฌํ ๋ ์ฑ๋ฅ์ด ๋จ์ด์ง ์ ์์ง๋ง, DB์ปค๋ฅ์ ์ ์งง๊ฒ ์ ์งํ๊ณ ์์ ์ ์ธ Paging ๋ฐฉ์์ ํํ๊ณ , JPA๋ฅผ ์ฌ์ฉ์ค์ด์๊ธฐ ๋๋ฌธ์ JpapagingItemReader ๋ฅผ ์ ํํ๋ค.
JpapagingItemReader ๋ก Item์ ์ฝ์ด๋๋ฆฌ๋ ๊ฐ๋จํ ์์ ์ฝ๋์ด๋ค.
@Bean
public ItemReader<Apply> applyReader() {
LocalDate tomorrow = LocalDate.now().plusDays(1);
return new RepositoryItemReaderBuilder<Apply>()
.name("applyReader")
.repository(applyRepository)
.methodName("findNeedToCancel")
.pageSize(CHUNK_SIZE)
.arguments(List.of(tomorrow))
.sorts(Collections.singletonMap("id", Sort.Direction.ASC)) // ํ์
.build();
}
์์์๋ ์ธ๊ธํ๋ฏ์ด ์ ๋ ฌ์ ๋ฌด์กฐ๊ฑด ํฌํจ๋์ด์ผ ํ๋ค.
๋๋์ ๋ฐ์ดํฐ๋ฅผ ์ ๋ ฌํ๋ ค๋ฉด full scan์ด ์ผ์ด๋๊ธฐ ๋๋ฌธ์ pk๋ฅผ ๊ธฐ์ค์ผ๋ก ์ ๋ ฌํ์ฌ ์ฑ๋ฅ์ ์ง์ฅ์ด ๊ฐ์ง ์๋๋ก ์ฝ๋๋ฅผ ์์ฑํ์๋ค.
6. JPAPagingItemReader - RepositoryItemReaderBuilder ์ฌ์ฉ ์ ์ฃผ์์ฌํญ
JPAPagingItemReader๋ฅผ ์ฌ์ฉํ๊ธฐ ์ํด์ ๋ช ๊ฐ์ง ์ฃผ์ํด์ผ ํ ๋ถ๋ถ๋ค์ด ์๋ค.
1. Hibernate, JPA ๋ฑ ์์์ฑ ์ปจํ ์คํธ๊ฐ ํ์ํ Reader ์ฌ์ฉ์ fetchSize์ ChunkSize๋ ๊ฐ์ ๊ฐ์ ์ ์งํด ์ผ ํ๋ค.
2. Pagepable์ ๊ผญ ๋ฃ์ด์ฃผ์ด์ผ ํ๋ค. ๋น์ฐํ Paging์ ์ฌ์ฉํด์ ์ฝ์ด์ค๊ธฐ ๋๋ฌธ์ Paging์ ํ๋ผ๋ฏธํฐ๋ก ์ก์์ฃผ์ง ์์ผ๋ฉด ์๋ฌ๊ฐ ๋ฌ๋ค.
3. 2๋ฒ๊ณผ ๋น์ทํ๊ฒ ๋ฐํ ํ์ ์ Page์ฌ์ผ ํ๋ค. List๋ก ์ค์ ํ ์ ์๋ฌ๊ฐ ๋ฌ๋ค.
4. where ์กฐ๊ฑด ๋ฌธ์ ๋ค์ด๊ฐ๋ ํ๋์ ๊ฐ์ด ๋ฐ๋ ๊ฒฝ์ฐ ๋ฐ์ดํฐ ๋๋ฝ ํ์์ด ๋ฐ์ํ ์ ์๋ค. ์ด ๋ถ๋ถ์ ๋ด์ฉ์ด ๊ธธ์ด์ ธ ์๋ ๊ธ์์ ๋ค๋ฃจ์๋ค.
https://dgjinsu.tistory.com/47