@Test
void signup_λ°±λ§κ±΄μ_μ μ κ°μ
() {
List<User> users = new ArrayList<>();
String password = passwordEncoder.encode("1234");
for(int i=0; i<1000000; i++) {
users.add(User.of("hong"+i+"@gmail.com", password, nicknameGenerate(), UserRole.USER));
}
userRepository.saveAll(users);
}
λ¨μν saveAll()μ 100λ§ κ±΄μ ν λ²μ λκΈ°λ©΄ μ΄λ»κ² λ κΉ κΆκΈν΄μ μ€νν΄λ΄€λ€.
μμλλ‘ java.lang.OutOfMemoryError: Java heap space μμΈκ° λ°μνλ©° ν
μ€νΈλ μ½ 3λΆ λ§μ μ€ν¨νλ€.
μμμ± μ»¨ν
μ€νΈκ° λΉμμ§μ§ μμΌλ©΄μ λ©λͺ¨λ¦¬κ° λμ λ κ²μ΄ μμΈμ΄μλ€.
κ²°κ³Ό: BUILD FAILED in 3m 17ssaveAll() νΈμΆλ§λ€ μμμ± μ»¨ν
μ€νΈλ₯Ό λΉμμ£Όλ©΄ λ©λͺ¨λ¦¬ λμλ₯Ό λ°©μ§ν μ μμ κ±°λΌ νλ¨νλ€.
@Test
void signup_λ°±λ§κ±΄μ_μ μ κ°μ
() {
List<User> users = new ArrayList<>();
String password = passwordEncoder.encode("1234");
int cnt = 1;
for(int i=0; i<100; i++) {
for(int ii=0; ii<10000; ii++) {
users.add(User.of("hong"+ cnt, password, nicknameGenerate(), UserRole.USER));
cnt ++;
}
userRepository.saveAll(users);
users.clear();
}
}
μμλλ‘ λ¬Έμ μμ΄ μ±κ³΅νλ€.
λ©λͺ¨λ¦¬λ κ³Όνκ² μ°¨μ§νμ§ μμκ³ μ±λ₯λ μ€μνλ€.
κ²°κ³Ό: BUILD SUCCESSFUL in 2m 51sλ μμ λ¨μλ‘ λλλ©΄ μλ μ°¨μ΄κ° μμκΉ?
@Test
void signup_λ°±λ§κ±΄μ_μ μ κ°μ
() {
List<User> users = new ArrayList<>();
String password = passwordEncoder.encode("1234");
int cnt = 1;
for(int i=0; i<1000; i++) {
for(int ii=0; ii<1000; ii++) {
users.add(User.of("hong"+ cnt +"@gmail.com", password, nicknameGenerate(), UserRole.USER));
cnt ++;
}
userRepository.saveAll(users);
users.clear();
}
}
μλλ μ½κ° λλ €μ‘μ§λ§ ν° μ°¨μ΄λ μμλ€.
κ²°κ³Ό: BUILD SUCCESSFUL in 2m 55ssaveAll() νΈμΆ νμκ° λ§μμ§μλ‘ μ€λ²ν€λλ μ¦κ°ν μ μλ€.
νμ§λ§ 무μμ λ§μ μμ ν λ²μ λ°μ΄λ£μΌλ©΄ μμ€ν
μ κ³ΌλΆνκ° μκΈΈ μ μμΌλ μ μ ν ν¬κΈ°μ λ¨μκ° μ€μνλ€.
@Test
@Transactional
@Rollback(false)
void signup_λ°±λ§κ±΄μ_μ μ κ°μ
() {
String password = passwordEncoder.encode("1234");
int cnt = 1;
for(int i=0; i<100; i++) {
for(int ii=0; ii<10000; ii++) {
entityManager.persist(User.of("hong"+ cnt +"@gmail.com", password, nicknameGenerate(), UserRole.USER));
cnt ++;
}
entityManager.flush();
entityManager.clear();
}
}
μν°ν° λ§€λμ λ₯Ό μ§μ μ¬μ©ν΄λ ν° μ°¨μ΄λ μμκ³ μ±λ₯μ saveAll()κ³Ό μ μ¬νλ€.
κ²°κ³Ό: BUILD SUCCESSFUL in 2m 55s @Test
@Transactional
@Rollback(false)
void signup_λ°±λ§κ±΄μ_μ μ κ°μ
_Jdbc() {
String sql = "insert into users (email, password, nickname, user_role, created_at, modified_at) values (?, ?, ?, ?, ?, ?)";
List<User> users = new ArrayList<>();
String password = passwordEncoder.encode("1234");
int cnt = 1;
for(int i=0; i<100; i++) {
for(int ii = 0; ii<10000; ii++) {
users.add(User.of("hong" + cnt + "@gmail.com", password, nicknameGenerate(), UserRole.USER));
cnt ++ ;
}
jdbcTemplate.batchUpdate(sql, users, users.size(),
(ps, user) -> {
ps.setString(1, user.getEmail());
ps.setString(2, user.getPassword());
ps.setString(3, user.getNickname());
ps.setString(4, user.getUserRole().name());
ps.setTimestamp(5, Timestamp.valueOf(LocalDateTime.now()));
ps.setTimestamp(6, Timestamp.valueOf(LocalDateTime.now()));
});
users.clear();
}
}
μ΄ λ°©μμ μ΄μ λ°©μλ€μ λΉν΄ ν¨μ¬ λΉ λ₯΄κ³ ν¨μ¨μ μ΄μλ€.
μ½ 1λΆ 30μ΄ μ λμ μ±λ₯ ν₯μμ΄ μμλ€.
κ²°κ³Ό: BUILD SUCCESSFUL in 1m 27s| λ°©μ | μμ μκ° | λΉκ³ |
|---|---|---|
| saveAll(100λ§κ±΄ ν λ²) | μ€ν¨ | OutOfMemoryError |
| saveAll(10,000건 Γ 100) | 2m 51s | μμ μ |
| saveAll(1,000건 Γ 1,000) | 2m 55s | μ½κ° λλ¦Ό |
| EntityManager μ§μ μ¬μ© | 2m 55s | μ±λ₯ μ μ¬ |
| JdbcTemplate Batch | 1m 27s | κ°μ₯ λΉ λ¦ |
100λ§ κ±΄μ μ¬μ©μ λ°μ΄ν°λ₯Ό κΈ°λ°μΌλ‘ nickname 쑰건μ μ΄μ©ν μ‘°ν μ±λ₯ ν
μ€νΈλ₯Ό μ§ννμλ€.
μ²μμλ λ¨μν JPA 쿼리 λ©μλλ₯Ό νμ©ν λ°©μμΌλ‘ μΈ‘μ νμκ³ μ΄ν μ±λ₯ ν₯μμ μν΄ μΈλ±μ€ μ μ©κ³Ό νλ‘μ μ
νμ© λ±μ 리ν©ν λ§μ λ¨κ³μ μΌλ‘ μννμλ€.
μ‘°ν κΈ°λ₯μ λν ν μ€νΈλ ν¬μ€νΈλ§¨μ νμ©ν ν΅ν© ν μ€νΈλ‘ μ§ννμλ€.
//리ν¬μ§ν°λ¦¬ λ©μλ
List<User> findByNickname(String nickname);
//μλΉμ€ λ©μλ
public List<UserResponse> findUserByNickname(UserNicknameRequest userNicknameRequest) {
List<User> foundUsers = userRepository.findByNickname(userNicknameRequest.getNickname());
return foundUsers
.stream()
.map(user -> UserResponse.of(user.getId(), user.getEmail(), user.getNickname())).toList();
}
JPA κ° μ 곡νλ κΈ°λ³Έ 쿼리 λ©μλλ₯Ό μ¬μ©νμ¬ nickname μΌλ‘ μ¬μ©μλ₯Ό μ‘°ννκ³ μ΄λ₯Ό DTO λ‘ λ³ννλ λ°©μμ΄λ€.
λ¨μνκ³ μ§κ΄μ μ΄μ§λ§ λΆνμνκ² λͺ¨λ 컬λΌμ μ‘°ννκΈ° λλ¬Έμ μ±λ₯μμ μ΄μκ° λ°μν μ μλ€.
μ‘°ν μκ°: 420msμ±λ₯ κ°μ μ μν΄ nickname 컬λΌμ μΈλ±μ€λ₯Ό μΆκ°νμλ€.
μΈλ±μ€λ₯Ό ν΅ν΄ κ²μ λ²μλ₯Ό λΉ λ₯΄κ² μ’ν μ μκΈ° λλ¬Έμ 쿼리 μν μκ°μ΄ λ¨μΆλλ€.
create index users_nickname_index on users (nickname);
μΈλ±μ€λ₯Ό μ μ©ν κ²°κ³Ό μ½ 17% μ λμ μ±λ₯ ν₯μμ νμΈν μ μμλ€.
μ‘°ν μκ°: 349msμΆκ°μ μΈ μ΅μ νλ₯Ό μν΄ JPA νλ‘μ μ
κΈ°λ₯μ νμ©ν΄ νμν 컬λΌλ§ μ‘°ννλλ‘ κ°μ νμλ€.
μν°ν° μ 체λ₯Ό μ‘°ννμ§ μκ³ DTO λ‘ μ§μ λ§€νν¨μΌλ‘μ¨ μΏΌλ¦¬μ λΉμ©μ μ€μΌ μ μλ€.
//리ν¬μ§ν°λ¦¬ λ©μλ
@Query("select new org.example.expert.domain.user.dto.response.UserResponse(u.id, u.email, u.nickname) from User u "
+ "where u.nickname = :nickname")
List<UserResponse> findProjectedUsersByNickname(@Param("nickname") String nickname);
//μλΉμ€ λ©μλ
public List<UserResponse> findUserByNickname(UserNicknameRequest userNicknameRequest) {
return userRepository.findProjectedUsersByNickname(userNicknameRequest.getNickname());
}
μ΅μ νλ₯Ό ν΅ν΄ μ 체μ μΌλ‘ μ½ 45%μ μ±λ₯ κ°μ μ μ΄λ£¨μλ€.
μ‘°ν μκ°: 229ms| λ¨κ³ | μ£Όμ κ°μ μ¬ν | μ‘°ν μκ° |
|---|---|---|
| 1λ¨κ³ κΈ°λ³Έ μ‘°ν | JPA 쿼리 λ©μλ μ¬μ© | 420ms |
| 2λ¨κ³ μΈλ±μ€ μ μ© | nickname 컬λΌμ μΈλ±μ€ μΆκ° | 349ms |
| 3λ¨κ³ νλ‘μ μ μ μ© | νμν 컬λΌλ§ μ‘°ννμ¬ DTO μ§μ λ°ν | 229ms |
λμ©λ λ°μ΄ν° μ μ₯ μ λ°°μΉ μ¬μ΄μ¦λ₯Ό μ μ ν λλκ³ JdbcTemplate λ°°μΉ μ²λ¦¬λ₯Ό νμ©νλ κ² κ°μ₯ ν¨μ¨μ΄ μ’λ€κ³ νλ¨λλ€.
μ‘°νμ κ²½μ°λ μΈλ±μ€ μ μ©κ³Ό νλ‘μ μ
μΌλ‘ νμν 컬λΌλ§ μ‘°ννλ©΄ μ±λ₯μ΄ ν¬κ² κ°μ λλ€.
μ€μ μλΉμ€ νκ²½μμ μ΅μ νλ μ¬μ©μμ μ λ’°μ μ°κ²°λλ νμμ μΈ μμλ‘ λμ©λμ λ°μ΄ν°λ₯Ό λ€λ£¨λ λ°©μμ λν΄ κ³μν΄μ κ³ λ―Όνκ³ νμ΅ν΄μΌκ² λ€.
κ·Έλ₯ κΆκΈμ¦μΈλ° μ΄μ€ 루νμμ λ λ²μ§Έ μΈλ±μ€λ₯Ό jκ° μλ iiλ‘ μ°λ 건 μ΄λμμ μ£Όλ‘ μ°μ΄λ 컨벀μ μΈκ°μ?