Skip to content

Latest commit

 

History

History
405 lines (304 loc) · 12.9 KB

데이터베이스 락과 캐싱을 이용한 성능 개선 방안 분석.md

File metadata and controls

405 lines (304 loc) · 12.9 KB

데이터베이스 락과 캐싱을 이용한 성능 개선 방안 분석

1. 서론

1.1 과제 목적

  • 목표: 데이터베이스의 락과 트랜잭션을 최대한 활용하여 동시성 문제를 해결하고, 캐싱과 Redis를 이용하여 시스템의 성능을 개선할 수 있는 부분을 깊이 있게 연구합니다.
  • 내용: 동시성 이슈 파악, 캐싱 및 Redis 적용 대상 선정, 적용 방안 및 합리적인 이유를 분석하고 정리합니다.

1.2 중요성

  • 시스템 성능 향상: 대용량 트래픽을 처리하기 위해서는 데이터베이스 부하를 줄이고 응답 속도를 향상시키는 것이 중요합니다.
  • 사용자 경험 개선: 빠른 응답 속도는 사용자 만족도를 높이고 서비스의 경쟁력을 강화합니다.

2. 현재 시스템의 문제점 분석

2.1 데이터베이스 부하

  • 문제점: 콘서트 정보 조회, 좌석 정보 조회, 대기열 토큰 관리 등에서 데이터베이스에 빈번한 접근이 발생하여 부하가 증가하고 있습니다.
  • 영향:
    • 응답 속도 저하
    • 데이터베이스 리소스 과다 사용
    • 동시성 이슈로 인한 데이터 무결성 문제 발생 가능성

2.2 응답 속도 저하

  • 문제점: 대기열에서 많은 사용자가 동시에 접속할 경우 응답 속도가 느려지고, 사용자 경험이 저하됩니다.
  • 영향:
    • 사용자 이탈 증가
    • 서비스 신뢰도 하락

3. 캐싱 및 Redis 적용 대상 선정

3.1 캐싱 대상 분석

3.1.1 콘서트 정보 조회

  • 특징:
    • 변경 빈도가 낮음
    • 다수의 사용자가 공통으로 조회
  • 선정 이유:
    • 캐싱을 통해 데이터베이스 부하를 크게 줄일 수 있음
    • 높은 캐시 히트율 기대

3.1.2 좌석 정보 조회

  • 특징:
    • 특정 시간대에 조회 요청 집중
    • 좌석 상태는 실시간으로 변경됨
  • 선정 이유:
    • 좌석의 기본 정보(좌석 번호, 가격 등)는 캐싱 가능
    • 좌석 상태는 캐싱하지 않음으로써 데이터 일관성 유지

3.1.3 대기열 토큰 관리

  • 특징:
    • 대기열 토큰은 일시적인 데이터
    • 빠른 읽기/쓰기가 필요함
  • 선정 이유:
    • Redis를 활용하여 빠른 성능 제공
    • TTL 설정을 통해 자동 만료 관리 가능

3.2 적용 우선순위

  1. 대기열 토큰 관리에 Redis 적용
  2. 콘서트 정보 조회 캐싱
  3. 좌석 정보 조회 캐싱

4. 적용 방안 및 합리적인 이유 제시

4.1 대기열 토큰 관리에 Redis 적용

4.1.1 현황 및 문제점

  • 현황: 대기열 토큰을 데이터베이스에 저장하여 관리 중
  • 문제점:
    • 데이터베이스 부하 증가
    • 대기열 처리 속도 저하

4.1.2 적용 방안

  • Redis 사용:
    • 대기열 토큰을 Redis에 저장
    • 토큰에 TTL(Time To Live)을 설정하여 자동 만료 관리
  • 데이터 구조:
    • Redis의 **Sorted Set(ZSet)**을 사용하여 대기열 순서를 관리
    • 사용자 ID를 멤버로, 입장 시간을 점수(score)로 설정

4.1.3 합리적인 이유

  • 성능 향상:
    • Redis는 메모리 기반의 데이터 저장소로, 매우 빠른 읽기/쓰기 성능을 제공합니다.
  • DB 부하 감소:
    • 데이터베이스 대신 Redis를 사용함으로써 DB 부하를 줄입니다.
  • TTL 관리 용이:
    • Redis는 TTL 설정을 지원하여 일시적인 데이터 관리에 적합합니다.
  • 캐시 지역성 활용:
    • 대기열에 자주 접근하는 사용자들의 데이터를 Redis에 저장하여 빠른 응답 제공

4.2 콘서트 정보 조회 캐싱

4.2.1 현황 및 문제점

  • 현황: 콘서트 정보 조회 시마다 데이터베이스를 조회
  • 문제점:
    • 다수의 사용자가 동시에 조회할 경우 데이터베이스 부하 증가
    • 응답 속도 저하

4.2.2 적용 방안

  • Redis 캐싱:
    • 콘서트 정보를 Redis에 캐싱
    • 캐시 키: concert:{concertId}
  • TTL 설정:
    • 콘서트 정보의 변경 주기가 길므로 긴 TTL 설정 가능
  • 캐시 무효화 전략:
    • 콘서트 정보 변경 시 해당 캐시 키 삭제 또는 갱신

4.2.3 합리적인 이유

  • DB 부하 감소:
    • 캐시 히트 시 데이터베이스 접근을 피할 수 있습니다.
  • 응답 속도 향상:
    • 메모리에서 데이터를 가져오기 때문에 빠른 응답이 가능합니다.
  • 캐시 지역성 활용:
    • 동일한 콘서트 정보를 여러 사용자가 조회하므로 캐시 효율이 높습니다.

4.3 좌석 정보 조회 캐싱

4.3.1 현황 및 문제점

  • 현황: 좌석 정보 조회 시마다 데이터베이스를 조회
  • 문제점:
    • 특정 시간대에 조회 요청이 집중되어 데이터베이스 부하 증가
    • 좌석 상태를 실시간으로 확인해야 하므로 캐싱 어려움

4.3.2 적용 방안

  • 기본 정보 캐싱:
    • 좌석 번호, 가격 등 변경되지 않는 정보를 캐싱
    • 캐시 키: seat:{concertId}:{seatNumber}:info
  • 좌석 상태는 캐싱하지 않음:
    • 실시간성이 요구되는 좌석 상태는 데이터베이스에서 직접 조회

4.3.3 합리적인 이유

  • 응답 속도 향상:
    • 좌석의 기본 정보를 캐싱하여 빠르게 제공
  • 데이터 일관성 유지:
    • 좌석 상태는 실시간 조회로 일관성 보장
  • 캐시 지역성 활용:
    • 특정 콘서트의 좌석 정보를 여러 사용자가 조회하므로 캐시 효율 높음

5. 성능 개선 효과 분석

5.1 대기열 토큰 관리에 Redis 적용 효과

  • DB 쿼리 감소:
    • 대기열 토큰 관리를 데이터베이스에서 Redis로 이관하여 DB 쿼리 횟수 감소
  • 응답 속도 개선:
    • Redis의 빠른 성능으로 대기열 처리 속도 향상
  • 부하 분산:
    • 데이터베이스에 집중된 부하를 Redis로 분산하여 시스템 안정성 향상

5.2 콘서트 정보 조회 캐싱 효과

  • 캐시 히트율 증가:
    • 콘서트 정보는 변경이 적으므로 캐시 히트율이 높게 유지
  • DB 부하 감소:
    • 캐시에서 데이터를 제공하여 데이터베이스 접근 감소
  • 응답 속도 향상:
    • 평균 응답 시간 단축 예상 (예: 200ms → 50ms)

5.3 좌석 정보 조회 캐싱 효과

  • 응답 속도 개선:
    • 좌석 기본 정보를 캐싱하여 조회 속도 향상
  • 데이터 일관성 유지:
    • 좌석 상태는 실시간 조회로 일관성 유지
  • 캐시 효율성:
    • 특정 시간대에 집중된 조회 요청을 효과적으로 처리

6. 구현 계획 및 코드 예시

6.1 대기열 토큰 관리 Redis 적용

6.1.1 Redis 설정 및 의존성 추가

  • Spring Boot Redis Starter 의존성 추가

    <!-- build.gradle 또는 pom.xml -->
    dependencies {
        implementation 'org.springframework.boot:spring-boot-starter-data-redis'
    }
  • Redis 설정

    # application.yaml
    spring:
      redis:
        host: localhost
        port: 6379

6.1.2 대기열 서비스 구현

  • 대기열 서비스 인터페이스

    interface QueueService {
        fun addToQueue(userId: Long, concertId: Long): Boolean
        fun isUserInQueue(userId: Long, concertId: Long): Boolean
        fun getUserPosition(userId: Long, concertId: Long): Long?
        fun removeFromQueue(userId: Long, concertId: Long)
    }
  • Redis를 이용한 구현

    @Service
    class RedisQueueService(
        private val redisTemplate: StringRedisTemplate
    ) : QueueService {
    
        private fun getQueueKey(concertId: Long): String = "queue:$concertId"
    
        override fun addToQueue(userId: Long, concertId: Long): Boolean {
            val key = getQueueKey(concertId)
            val score = System.currentTimeMillis().toDouble()
            return redisTemplate.opsForZSet().add(key, userId.toString(), score)
        }
    
        override fun isUserInQueue(userId: Long, concertId: Long): Boolean {
            val key = getQueueKey(concertId)
            return redisTemplate.opsForZSet().rank(key, userId.toString()) != null
        }
    
        override fun getUserPosition(userId: Long, concertId: Long): Long? {
            val key = getQueueKey(concertId)
            val rank = redisTemplate.opsForZSet().rank(key, userId.toString())
            return rank?.plus(1) // Rank는 0부터 시작하므로 +1
        }
    
        override fun removeFromQueue(userId: Long, concertId: Long) {
            val key = getQueueKey(concertId)
            redisTemplate.opsForZSet().remove(key, userId.toString())
        }
    }
  • TTL 설정

    override fun addToQueue(userId: Long, concertId: Long): Boolean {
        val key = getQueueKey(concertId)
        val score = System.currentTimeMillis().toDouble()
        val added = redisTemplate.opsForZSet().add(key, userId.toString(), score)
        redisTemplate.expire(key, Duration.ofMinutes(30)) // 30분 TTL 설정
        return added
    }

6.2 콘서트 정보 조회 캐싱 구현

  • 캐시 설정

    spring:
      cache:
        type: redis
  • 캐시 어노테이션 사용

    @Service
    class ConcertService(
        private val concertRepository: ConcertRepository
    ) {
        @Cacheable(value = ["concert"], key = "#concertId")
        fun getConcertById(concertId: Long): Concert {
            return concertRepository.findById(concertId)
                ?: throw ResourceNotFoundException("Concert not found")
        }
    }
  • 캐시 무효화

    @Service
    class ConcertService(
        private val concertRepository: ConcertRepository
    ) {
        @CachePut(value = ["concert"], key = "#concert.id")
        fun updateConcert(concert: Concert): Concert {
            return concertRepository.save(concert)
        }
    
        @CacheEvict(value = ["concert"], key = "#concertId")
        fun deleteConcert(concertId: Long) {
            concertRepository.deleteById(concertId)
        }
    }

6.3 좌석 정보 조회 캐싱 구현

  • 좌석 기본 정보 캐싱

    @Service
    class SeatService(
        private val seatRepository: SeatRepository
    ) {
        @Cacheable(value = ["seatInfo"], key = "#concertId + ':' + #seatNumber")
        fun getSeatInfo(concertId: Long, seatNumber: Int): SeatInfo {
            val seat = seatRepository.findByConcertIdAndSeatNumber(concertId, seatNumber)
                ?: throw ResourceNotFoundException("Seat not found")
            return SeatInfo(seat.seatNumber, seat.price)
        }
    }
  • 좌석 상태 실시간 조회

    @Service
    class SeatService(
        private val seatRepository: SeatRepository
    ) {
        fun getSeatStatus(concertId: Long, seatNumber: Int): SeatStatus {
            val seat = seatRepository.findByConcertIdAndSeatNumber(concertId, seatNumber)
                ?: throw ResourceNotFoundException("Seat not found")
            return seat.status
        }
    }

7. 결론

7.1 기대 효과

  • 시스템 성능 향상:
    • Redis를 활용하여 데이터베이스 부하를 줄이고, 응답 속도를 향상시킬 수 있습니다.
  • 사용자 경험 개선:
    • 빠른 응답으로 사용자 만족도가 높아집니다.
  • 시스템 안정성 향상:
    • 부하 분산을 통해 시스템의 안정성을 높일 수 있습니다.

7.2 향후 계획

  • Redis 모니터링 및 장애 대응 방안 마련:
    • Redis 장애 시 대비책을 마련하여 SPOF 문제를 해결합니다.
  • 캐싱 전략 고도화:
    • 캐시 무효화 정책 및 TTL 최적화를 통해 캐시 효율을 높입니다.
  • DB 인덱싱 및 최적화 병행:
    • 캐싱과 함께 데이터베이스의 인덱싱 등 최적화를 진행하여 근본적인 성능 개선을 도모합니다.

참고 문헌 및 자료


부록

A. 캐싱 적용 시 고려사항 정리

  • 데이터 일관성 유지:
    • 캐싱된 데이터와 데이터베이스 간의 일관성을 유지하기 위한 전략 필요
  • TTL 설정:
    • 데이터 특성에 맞는 적절한 TTL 설정으로 캐시 효율 극대화
  • 캐시 무효화 전략:
    • 데이터 변경 시 캐시를 어떻게 갱신하거나 무효화할지에 대한 명확한 정책 수립

B. Redis 사용 시 주의사항

  • SPOF 문제 해결:
    • Redis Sentinel 또는 Cluster를 이용한 고가용성 구성
  • 모니터링 도구 활용:
    • Redis의 성능과 상태를 모니터링하여 이슈 발생 시 신속 대응
  • 적절한 메모리 관리:
    • Redis는 메모리 기반이므로, 메모리 사용량을 주기적으로 확인하고 관리 필요