
๋ฐ์ดํฐ๋ฒ ์ด์ค ์ฑ๋ฅ์ ํต์ฌ์ ๋์คํฌ์ ๋๋ค I/O(์ ๊ทผ)์ ์ต์ํ์ํค๋ ๊ฒ์ด๋ค.
์ธ๋ฑ์ค๋ฅผ ํ์ฉํ๋ฉด ์กฐํ๋ฅผ ์ต์ ํ์ํฌ ์ ์๋ค.
์ธ๋ฑ์ค
์ธ๋ฑ์ค๋ ์ ๋ ฌ๋ ์๋ฃ๊ตฌ์กฐ๋ก ์ด๋ฅผ ํตํด ํ์๋ฒ์๋ฅผ ์ต์ํํ ์ ์๋ค.
์ธ๋ฑ์ค๋ ํ ์ด๋ธ์ด๊ธฐ ๋๋ฌธ์ ์ฟผ๋ฆฌ๊ฐ ๋ ์์ค๋ฉด ์ธ๋ฑ์ค๋ฅผ ์กฐํํ ํ ์ฐพ์ ๊ฒฐ๊ณผ๋ฅผ ๋ค์ ์๋ณธ๋ฐ์ดํฐ๋ก ๋์๊ฐ ์ป๊ฒ ๋๋ค.
์ธ๋ฑ์ค ์๋ฃ๊ตฌ์กฐ
- HashMap
- ๋จ๊ฑด ๊ฒ์ ์๋ โ O(1)
- ๋ฒ์ ํ์ โ O(N)
- like 'AB%' ์ ๊ฐ์ ์ฟผ๋ฆฌ๋ฌธ์ ์ฌ์ฉํ ๋ ํค๋ฅผ ํ๋ํ๋ ๊บผ๋ด์ ๋น๊ตํด์ผํจ
- List
- ์ ๋ ฌ๋์ง ์์ ๋ฆฌ์คํธ ํ์ โ O(N)
- ์ ๋ ฌ๋ ๋ฆฌ์คํธ ํ์ โ O(logN)
- ์ ๋ ฌ๋์ง ์์ ๋ฆฌ์คํธ์ ์ ๋ ฌ ์๊ฐ ๋ณต์ก๋ โ O(N) ~ O(N*logN)
- ์ฝ์ ๋ฐ ์ญ์ ๋น์ฉ ๋งค์ฐ ๋์
- Tree
- ํธ๋ฆฌ ๋์ด์ ๋ฐ๋ผ ์๊ฐ ๋ณต์ก๋๊ฐ ๊ฒฐ์ ๋จ ๐ ํธ๋ฆฌ์ ๋์ด ์ต์ํํ๋ ๊ฒ์ด ์ค์
- ํ์ชฝ์ผ๋ก ๋ ธ๋๊ฐ ์น์ฐ์น์ง ์๋๋ก ๊ท ํ ์ก์์ฃผ๋ ํธ๋ฆฌ ์ฌ์ฉ โ B+ Tree
- B+ Tree
- ์ฝ์ ๋ฐ ์ญ์ ์ ํญ์ ๊ท ํ์ ์ด๋ฃธ
- ํ๋์ ๋ ธ๋๊ฐ ์ฌ๋ฌ ๊ฐ์ ์์ ๋ ธ๋๋ฅผ ๊ฐ์ง ์ ์์
- ๋ฆฌํ๋ ธ๋์๋ง ๋ฐ์ดํฐ ์กด์ฌ ๐ ์ฐ์์ ์ธ ๋ฐ์ดํฐ ์ ๊ทผ ์ ์ ๋ฆฌ
ํด๋ฌ์คํฐ ์ธ๋ฑ์ค
- ํด๋ฌ์คํฐ ์ธ๋ฑ์ค๋ ๋ฐ์ดํฐ ์์น๋ฅผ ๊ฒฐ์ ํ๋ ํค ๊ฐ์ด๋ค
- ์ ๋ ฌ๋ ์๋ฃ๊ตฌ์กฐ๋ก ํด๋ฌ์คํฐ ํค ์์น์ ๋ฐ๋ผ์ ๋ฐ์ดํฐ ์ฃผ์ ๊ฒฐ์ ๋จ.
- ํด๋ฌ์คํฐ ํค ์ฝ์ /๊ฐฑ์ ์ ์ฑ๋ฅ ์ด์ ๋ฐ์
- MySQL์ PK๋ ํด๋ฌ์คํฐ ์ธ๋ฑ์ค๋ค.
๐ก Auto Increment vs UUID
๐น Auto Increment : ๋น ๋ฅด๊ณ Key๋ฅผ ํ๋์ ๋ณด๊ธฐ ์ฌ์ฐ๋ ๋ถ์ฐ ์์คํ ์ ์ ํฉํ์ง ์์.
๐น UUID : ์ด๋์๋ ์ง ๋ง๋ค๊ณ ๊ณ ์ ์ฑ์ ๋ณด์ฅํ ์ ์์ด ๋ถ์ฐ ์์คํ ์ ์ฉ์ดํ๋ ์ฑ๋ฅ์ ์ ํ ๋ฐ์ (ํด๋ฌ์คํฐ ์ธ๋ฑ์ค์ ์ ๋ ฌ ๋น์ฉ ํผ)
- MySQL์์ PK๋ฅผ ์ ์ธํ ๋ชจ๋ ์ธ๋ฑ์ค๋ PK๋ฅผ ๊ฐ์ง๊ณ ์๋ค.
- PK๋ ํด๋ฌ์คํฐ ์ธ๋ฑ์ค์ด๊ธฐ ๋๋ฌธ์ ๋ฐ์ดํฐ ์ฝ์ /์ญ์ ์ PK์ ํด๋นํ๋ ๋ฐ์ดํฐ ์ฃผ์๊ฐ ๋ณ๊ฒฝ๋๋ค. ๋ง์ฝ ์ธ๋ฑ์ค๊ฐ PK๊ฐ ์๋ ๋ฐ์ดํฐ ์ฃผ์๋ฅผ ๊ฐ์ง๊ณ ์๋ค๋ฉด ๋ฐ์ดํฐ ์์๊ฐ ๋ฐ๋ ๋ ์ธ๋ฑ์ค๋ ๊ฐ์ด ๊ฐฑ์ ๋์ด์ผํ๊ธฐ ๋๋ฌธ์ ๋ถํ๊ฐ ํฌ๋ค. ๊ทธ๋ฌ๋ฏ๋ก ์ธ๋ฑ์ค๋ PK๋ฅผ ๊ฐ์ง๊ณ ์๋ค !
Bulk Insert ๊ตฌํ
Spring Data JPA๋ฅผ ์ฌ์ฉํ๋ค๋ฉด list๋ฅผ ๋ฐ๋ insert๊ฐ saveAll()์ผ ๋ save์ ๋ฃจํ๋ฅผ ๋๋ฉด์ ํธ์ถํ๋ค.
Bulk insert๋ฅผ ํ๋ค๋ฉด JDBC๋ฅผ ์ฌ์ฉํ๋ ๊ฒ์ด ์ฑ๋ฅ์ ์ ๋ฆฌํ ์ ์๋ค.
@RequiredArgsConstructor
@Repository
public class PostRepository {
private final NamedParameterJdbcTemplate namedParameterJdbcTemplate;
private static final String TABLE = "post";
public void bulkInsert(List<Post> posts) {
var sql = String.format("""
INSERT INTO `%s` (memberId, contents, createdDate, createdAt)
VALUES (:memberId, :contents, :createdDate, :createdAt)
""", TABLE);
SqlParameterSource[] params = posts
.stream()
.map(BeanPropertySqlParameterSource::new)
.toArray(SqlParameterSource[]::new);
// batchUpdate -> ๋ฆฌ์คํธ ํํ์ SQL ์ฟผ๋ฆฌ์ ๋งค๊ฐ๋ณ์ ๋ฐฐ์ด๋ก ์ด๋ฃจ์ด์ง ์ธ์๋ฅผ ๋ฐ์๋ค์ฌ, ์ฌ๋ฌ ๊ฐ์ SQL ์ฟผ๋ฆฌ๋ฅผ ์ผ๊ด์ ์ผ๋ก ์คํ
namedParameterJdbcTemplate.batchUpdate(sql, params);
}
}
public class PostFixtureFactory {
static public EasyRandom get(Long memberId, LocalDate firstDate, LocalDate lastDate) {
var idPredicate = named("id")
.and(ofType(Long.class))
.and(inClass(Post.class));
var memberIdPredicate = named("memberId")
.and(ofType(Long.class))
.and(inClass(Post.class));
var param = new EasyRandomParameters()
.excludeField(idPredicate)
.dateRange(firstDate, lastDate)
.randomize(memberIdPredicate, () -> memberId);
return new EasyRandom(param);
}
}
@SpringBootTest
public class PostBulkInsertTest {
@Autowired
private PostRepository postRepository;
@Test
public void bulkInsert(){
var easyRandom = PostFixtureFactory.get(
3L,
LocalDate.of(2023, 11, 1),
LocalDate.of(2023, 12, 1));
var stopWatch = new StopWatch();
stopWatch.start();
var posts = IntStream.range(0, 1000000)
.parallel()
.mapToObj(i -> easyRandom.nextObject(Post.class))
.toList();
stopWatch.stop();
System.out.println("๊ฐ์ฒด ์์ฑ ์๊ฐ : " + stopWatch.getTotalTimeSeconds());
var queryStopWatch = new StopWatch();
queryStopWatch.start();
postRepository.bulkInsert(posts);
queryStopWatch.stop();
System.out.println("DB insert ์๊ฐ : " + queryStopWatch.getTotalTimeSeconds());
}
}
bulkInsert() ๊ฒฐ๊ณผ ์ฟผ๋ฆฌ๊ฐ ๋ฆฌ์คํธ๋ก ๋์ด๊ฐ๊ณ ํ๋๋ง ์์ฑ๋๋ ๊ฒ์ ํ์ธํ ์ ์๋ค.
100๋ง ๊ฑด์ ๋ฐ์ดํฐ๋ฅผ ์ฝ์ ํ๊ฒ ๋๋ฉด ๋ฉ๋ชจ๋ฆฌ ์ฌ์ด์ฆ ๋ถ์กฑ์ผ๋ก Java heap space ์๋ฌ๊ฐ ๋ฐ์ํ ์ ์๋ค.
์ด๋ด ๋๋ heap size๋ฅผ ๋๋ ค์ฃผ๋ฉด ๋๋ค. Help > Edit Custom VM options..์์ 2048 โ 4096์ผ๋ก ๋๋ ค์ฃผ์๋ค.
Preferences - Gradle- Run test using: IntelliJ IDEA ๋ก ์ค์ ํด์ผ ์ ์ฉ๋๋ค.
๊ทธ๋ฌ๋ฉด ์ค๋ฅธ์ชฝ ํ๋จ์ ๋์ด๋ ๋ฉ๋ชจ๋ฆฌ๋ฅผ ํ์ธํ ์ ์๋ค.
100๋ง ๊ฑด ๋ฐ์ดํฐ๋ฅผ Insert ํ ๋ DB insert ์๊ฐ์ ์ฝ 11์ด ์์๋๋ค.
๐ก EasyRandom
๐ Java ๊ฐ์ฒด๋ฅผ ๋๋ค์ผ๋ก ์์ฑํ๋ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ก, ์ ์ํ ํด๋์ค ํ์ ์ ๊ฐ์ฒด๋ฅผ ๋๋คํ๊ฒ ์์ฑํ๊ธฐ ๋๋ฌธ์ ํ ์คํธ ๋ฐ์ดํฐ๋ฅผ ์์ฑํ๊ฑฐ๋ ๋๋ฏธ๋ฐ์ดํฐ๋ฅผ ๋ง๋ค ๋ ์ฃผ๋ก ์ฌ์ฉํ๋ค. ๊ฐ์ฒด์ ํ๋๊ฐ ๋ง์ ๋ ์ผ์ผ์ด ๊ฐ์ ์ค์ ํ์ง ์์๋ ๋๊ธฐ ๋๋ฌธ์ ํธ๋ฆฌํ๋ค.
โ ๋ค์ํ ํ๋ผ๋ฏธํฐ๋ฅผ ์ ์ฉํด์ ์๋ง๊ฒ ์ฌ์ฉํ๋ฉด ๋๋ค.
โnextObject()
๋ฉ์๋๋ ์ฃผ์ด์ง ํด๋์ค์ ๊ฐ์ฒด๋ฅผ ์์ฑํ๊ณ ํด๋น ํด๋์ค์ ํ๋๋ฅผ ๋๋ค์ผ๋ก ์ฑ์์ ๋ฐํํ๋ค.
์ธ๋ฑ์ค ํ์ฉ ์ ์ฑ๋ฅ ์ฐจ์ด ๋น๊ต
create index POST__index_member_id
on POST (memberId);
create index POST__index_created_date
on POST (createdDate);
create index POST__index_member_id_created_date
on POST (memberId, createdDate);
POST ํ ์ด๋ธ์ index๋ฅผ ์ถ๊ฐํ๋ค.
memberId๋ 1 ~ 4๋ฒ๊น์ง ์กด์ฌํ๊ณ , createdDate์ 1900๋ 1์ ~ 2023๋ 12์๊น์ง ์กด์ฌํ๋ค.
memberId๊ฐ 3์ธ ํ์์ ์ฝ 200๋ง ๊ฑด insertํ ์ํ์ด๋ค.
๐น ์ธ๋ฑ์ค ์ฌ์ฉํ์ง ์๊ณ ์กฐํ ๐ ์ฝ 863ms ์์
๐น ์ธ๋ฑ์ค ์ฌ์ฉํ๊ณ ์กฐํ ๐ ์ฝ 4s ์์
๐ข ์ธ๋ฑ์ค๋ฅผ ํ์ฉํ๋๋ฐ, ์ฑ๋ฅ์ด ๋ ์ ํ๋ ์ด์
๐ ์ธ๋ฑ์ค๊ฐ ์์ ๋๋ ํ ์ด๋ธ๋ง ํ์ธํ๋ฉด ๋๋๋ฐ ์ธ๋ฑ์ค๊ฐ ์๋ ๊ฒฝ์ฐ, memberId๊ฐ 3๋ฒ์ ํด๋นํ๋ 200๋ง ๊ฑด์ ๋ฐ์ดํฐ๋ ์ธ๋ฑ์ค๋ ๋ณด๊ณ , ํ ์ด๋ธ๋ ํ์ธํด์ผํ๋ค. โ ์ธ๋ฑ์ค๊ฐ ํ์๋ฒ์๋ฅผ ์ขํ์ฃผ์ง ๋ชปํด ์๊ฐ 2๋ฐฐ ์ด์ ์์
๐ ๋ง์ฝ, createdDate ์ธ๋ฑ์ค๋ฅผ ํ์ฉํ๋ค๋ฉด ๋ฐ๋๋ก ์ฑ๋ฅ์ด ๊ฐ์ ๋๋ค.createdDate์ ์๋ณ๊ฐ์ ์ฝ 19700๊ฐ๋ก ๋ง์ด ๋ถํฌ๋์ด์๋ ๋ฐ๋ฉด, memberId๋ 4๊ฐ๋ฟ์ด๋ค. createdDate์ ์๋ณ๊ฐ์ด ์๋์ ์ผ๋ก ๊ณ ๋ฅด๊ฒ ๋ถํฌ๋์ด์๊ธฐ ๋๋ฌธ์ ๋ณด๋ค ๊ท ํ์ ์ธ ์ธ๋ฑ์ค ํธ๋ฆฌ๋ฅผ ์ด๋ฃฐ ๊ฒ์ด๋ฏ๋ก ์ฑ๋ฅ์ด ๊ฐ์ ๋๋ค.
๐น ๋ณตํฉ ์ธ๋ฑ์ค ์ฌ์ฉํด์ ์กฐํ ์ ๐ ์ฑ๋ฅ ๊ฐ์
๐ธ ๋ณตํฉ ์ธ๋ฑ์ค : memberId๋ก ๋จผ์ ํ์ ์์ํด์ ํ์ ๋ฒ์ ์ค์ โ createdDate ํ์
createdDate ์ธ๋ฑ์ค ํ๋๋ง ์ฌ์ฉํ์ ์์๋ ์ฝ 127ms ์์
๋ณตํฉ ์ธ๋ฑ์ค ์ฌ์ฉ์ ์ฝ 62ms ์์ ๐ 2๋ฐฐ ์ด์ ์ฑ๋ฅ ๊ฐ์
๐ก ์ธ๋ฑ์ค ์ฌ์ฉ ์ ๊ณ ๋ คํด์ผํ ์
1๏ธโฃ ํ๋ผ๋ฏธํฐ๋ก ๋ค์ด์ค๋ ๊ฒ์ ๋ฐ์ดํฐ ๋ถํฌ๋
2๏ธโฃ ์ด๋ค ์ปฌ๋ผ์ด orderBy, groupBy ๋์ด ์๋์ง ..
์ฐธ๊ณ
https://github.com/j-easy/easy-random
https://engineering.linecorp.com/ko/blog/mysql-workbench-visual-explain-index
'CS > ๐๏ธ Data' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
[Data] ๋ฐ์ดํฐ ํธ๋์ญ์ - @Transactional (0) | 2023.12.13 |
---|---|
[์๋ฃ๊ตฌ์กฐ] Priority Queue (์ฐ์ ์์ ํ) (0) | 2023.08.31 |