해커톤 메모

오젼·2024년 7월 28일
0

성능 개선을 위한 방법을 고려하는 것은 매우 중요한 접근입니다. 인덱스 사용은 성능 개선의 핵심 방법 중 하나입니다. 다음과 같은 방법으로 성능을 개선할 수 있습니다:

  1. 복합 인덱스 추가:
    user_id와 diary_date에 대한 복합 인덱스를 추가하면 조회 성능을 크게 향상시킬 수 있습니다.
@Entity
@Table(name = "diary", indexes = {
    @Index(name = "idx_user_date", columnList = "user_id,diary_date", unique = true)
})
public class Diary extends BaseEntity {
    // ... 기존 코드 ...
}
  1. 낙관적 락(Optimistic Locking) 사용:
    동시성 문제를 해결하면서도 성능을 유지하기 위해 낙관적 락을 사용할 수 있습니다.
@Entity
@Table(name = "diary")
public class Diary extends BaseEntity {
    // ... 기존 코드 ...

    @Version
    private Long version;
}
  1. 쿼리 최적화:
    JPA의 save 메서드 대신 더 효율적인 upsert 쿼리를 사용할 수 있습니다.
@Repository
public interface DiaryRepository extends JpaRepository<Diary, Long> {
    @Modifying
    @Query(value = "INSERT INTO diary (user_id, title, content, diary_date) " +
           "VALUES (:userId, :title, :content, :diaryDate) " +
           "ON DUPLICATE KEY UPDATE title = :title, content = :content", 
           nativeQuery = true)
    void saveOrUpdate(@Param("userId") Long userId, @Param("title") String title, 
                      @Param("content") String content, @Param("diaryDate") LocalDate diaryDate);
}
  1. 캐싱 도입:
    자주 접근하는 다이어리 데이터에 대해 캐싱을 적용할 수 있습니다.
@Cacheable("diaries")
public Diary getDiaryByUserAndDate(User user, LocalDate date) {
    // 구현 코드
}
  1. 페이징 처리:
    대량의 다이어리 데이터를 조회할 때 페이징을 적용하여 성능을 개선할 수 있습니다.
@GetMapping("/user/{userId}")
public ResponseEntity<Page<Diary>> getDiariesByUserId(
        @PathVariable Long userId, 
        @RequestParam(defaultValue = "0") int page, 
        @RequestParam(defaultValue = "20") int size) {
    PageRequest pageRequest = PageRequest.of(page, size, Sort.by("diaryDate").descending());
    Page<Diary> diaries = diaryService.getDiariesByUserId(userId, pageRequest);
    return ResponseEntity.ok(diaries);
}
  1. 비정규화 고려:
    조회 성능이 매우 중요하다면, 일부 데이터를 비정규화하여 조인 연산을 줄일 수 있습니다.

이러한 방법들을 적용하면 성능을 크게 개선할 수 있습니다. 특히 복합 인덱스 추가와 쿼리 최적화는 즉각적인 성능 향상을 가져올 수 있는 방법입니다. 실제 적용 시에는 애플리케이션의 특성과 데이터 접근 패턴을 고려하여 가장 적합한 방법을 선택하는 것이 중요합니다.

네, 맞습니다. Diary 엔티티에 user_id와 diary_date에 대한 복합 인덱스를 설정하는 것은 성능을 유의미하게 향상시킬 수 있는 좋은 방법입니다.

  1. 복합 인덱스의 효과:
    • 특정 사용자의 특정 날짜 다이어리를 빠르게 조회할 수 있습니다.
    • 사용자별 다이어리 목록을 날짜순으로 빠르게 정렬하여 조회할 수 있습니다.
    • 중복 입력을 방지할 수 있습니다 (unique 인덱스로 설정 시).

이제 낙관적 락과 캐싱에 대해 더 자세히 설명드리겠습니다:

  1. 낙관적 락 (Optimistic Locking):
    낙관적 락은 동시성 제어 메커니즘으로, 데이터 충돌이 드물게 발생한다는 가정하에 작동합니다.

    작동 방식:

    • 엔티티에 버전 필드(@Version)를 추가합니다.
    • 데이터를 읽을 때 현재 버전을 함께 읽습니다.
    • 데이터를 업데이트할 때, 현재 데이터베이스의 버전과 읽어온 버전이 일치하는지 확인합니다.
    • 버전이 일치하면 업데이트하고 버전을 증가시킵니다.
    • 버전이 다르면 다른 트랜잭션이 데이터를 변경한 것으로 간주하고 예외를 발생시킵니다.

    장점:

    • 데이터베이스 락을 사용하지 않아 성능 오버헤드가 적습니다.
    • 동시성 문제를 효과적으로 처리할 수 있습니다.

    단점:

    • 충돌 발생 시 애플리케이션에서 재시도 로직을 구현해야 합니다.

    구현 예:

    @Entity
    public class Diary {
        @Version
        private Long version;
        // 다른 필드들...
    }
  2. 캐싱 도입:
    캐싱은 자주 접근하는 데이터를 메모리에 저장하여 빠르게 조회할 수 있게 하는 기술입니다.

    작동 방식:

    • 처음 데이터를 조회할 때 데이터베이스에서 가져와 캐시에 저장합니다.
    • 이후 같은 데이터를 요청하면 데이터베이스 대신 캐시에서 가져옵니다.
    • 데이터가 변경되면 캐시를 갱신하거나 무효화합니다.

    장점:

    • 데이터베이스 접근을 줄여 응답 시간을 크게 단축할 수 있습니다.
    • 데이터베이스 부하를 줄일 수 있습니다.

    단점:

    • 캐시 일관성 관리가 필요합니다.
    • 메모리 사용량이 증가합니다.

    구현 예 (Spring의 @Cacheable 사용):

    @Service
    public class DiaryService {
        @Cacheable(value = "diaryCache", key = "#userId + '-' + #date")
        public Diary getDiary(Long userId, LocalDate date) {
            // 데이터베이스에서 다이어리 조회 로직
        }
    
        @CacheEvict(value = "diaryCache", key = "#diary.user.id + '-' + #diary.diaryDate")
        public void updateDiary(Diary diary) {
            // 다이어리 업데이트 로직
        }
    }

    이 예제에서는 getDiary 메서드 결과를 캐시하고, updateDiary 메서드에서 해당 캐시를 무효화합니다.

낙관적 락과 캐싱은 모두 성능 향상에 도움이 되지만, 적용 시 신중한 설계와 테스트가 필요합니다. 특히 캐싱의 경우, 캐시 무효화 전략과 메모리 사용량을 고려해야 합니다. 실제 애플리케이션의 특성과 요구사항에 따라 이러한 기술들을 적절히 조합하여 사용하면 좋은 성능 개선 효과를 얻을 수 있습니다.

0개의 댓글