2차 캐시

vs 1차 캐시

  • 1차 캐시는 영속성 컨텍스트를 통해 이루어진다.
  • 1차 캐시는 하나의 트랜잭션이 시작하고 종료할 때 까지만 유효하다.
  • OSIV(Open Session In View)를 이용하면 클라이언트의 요청이 들어오고 해당 요청이 끝날때 까지 1차 캐시를 유효하게하지만 애플리케이션 관점에서 DB에 대한 접근 횟수를 줄이기에는 역부족이다.

2차 캐시에 대하여

  • 애플리케이션 레벨에서 공유되므로 공유 캐시(shared cache)라 부르기도 한다.
  • 애플리케이션 종료시 까지 캐시가 유지된다.
  • 1차 캐시에 원하는 Entity가 없을 때 2차 캐시를 뒤지고, 이 때에도 없다면 DB를 조회헤 2차 캐시에 해당 Entity를 저장한다. 그리고 해당 Entity에 대한 복사본을 1차 캐시로 반환해준다. 이는 여러 곳에서 같은 객체를 동시에 수정하는 문제를 방지해준다.
  • Persistenct Unit 범위의 캐시이다.
  • 데이터베이스 기본키를 기준으로 캐시하지만, 영속성 컨텍스트가 다르면 객체의 동일성(a==b)을 보장하지 않는다.

설정방법

  1. @Cacheable
    @Entity
    public class Member {
       @Id @GeneratedValue
       private Long id;
    
       ...
    }

    와 같이 @Cacheable 어노테이션을 사용해준다.

  2. <persistencce-unit name="2ndCache">
       <shared-cache-mode>ENABLE_SELECTIVE</shared-cache-mode>
    </persistencce-unit>

    와 같이 persistence.xmlshared-cache-mode를 설정함으로써 persistence unit 단위 캐시를 적용해준다.

'Backend > SpringBoot-JPA' 카테고리의 다른 글

@RestController 어노테이션  (0) 2021.04.29
Optimistic/Pessimistic Lock  (1) 2021.04.19
@Transactional  (0) 2021.03.21

Garbage Collection Algorithm의 구성

Reference Counting Algorithm

  • 초기의 대표적인 GC 전략
  • 각 Object의 reference count를 관리
  • 특정 Object가 GC대상이 되면, 해당 Object에 의해 참조되는 Object의 count를 연쇄적으로 감소시킴

    장점 & 단점

  • (+) Garbage object 판단이 쉬움
  • (+) GC 대상인지 판단을 위한 STW가 발생하지 않음
  • (+) GC 구동 시점에 제약이 없어 실시간성 가능
  • (-) reference count 관리 비용 발생
  • (-) Circular Linked List와 같은 경우는 영원히 GC 대상이 되지 못함

Mark and Sweep Algorithm

  • Root set에서 시작해 deep dive하며 reference 관계를 추적함
  • Tracing algorithm이라고도 불림
  • Mark 단계에서는 Live object를 찾아 Mark 함. 이 때 Object 내 flag 또는 bitmap table을 이용함.
  • Sweep 단계에서는 Mark되지 않은 객체들을 제거함

    장점 & 단점

  • (+) Reference 관계를 정확히 파악할
  • (+) Reference 관계를 맺을 때 부가적인 Overhead가 없음
  • (-) STW 현상이 발생함
  • (-) Compaction의 부재로 Fragmentation이 발생함

Mark and Compaction Algorithm

  • Fragmentation 문제점과 이로인한 Memory 효율화를 개선하기 위한 알고리즘
  • Mark 단계에서는 Live object를 Mark
  • Compaction 단계에서는 Live object를 연속된 memory 공간에 차례로 쌓아둠
    • Compaction 단계에서 적재되는 Object들의 순서에 따라 Arbitrary(순서 보장x), Linear(인접한 Pointer 순), Sliding(Allocation 순) 방식이 있다.
  • 위 두 단계에서 Handle을 사용하는데, Handle은 Live/Dead를 구분하는 FlagObject가 저장되어있는 위치공간 주소로 구성된다. Mark 단계에서는 각 Obejct에 해당하는 Handle에 Object가 저장된 위치 공간을 기록하고, Compaction 단계에서는 Object 이동 후 Handle을 업데이트 시킨다.

    장점 & 단점

  • (+) Fragmentation을 방지 및 이로인한 Memory 효율성
  • (-) Reference 업데이트 시 Object를 접근하는데 따른 Overhead 존재
  • (-) STW 발생

Copying Algorithm

  • Stop the Copy algorithm이라고도 불린다.
  • Heap을 Active, Inactive영역으로 나누어 사용한다.
  • Active영역에만 Object가 생성되고, Active영역이 가득차면 Live Object만 Inactive영역으로 이동한다.

    장점 & 단점

  • (+) Framgentation을 방지 (Inactive영역으로 이동 시 차례로 적재)
  • (-) STW 발생
  • (-) Copy에 따른 Overhead 발생
  • (-) 공간활용(50% Active / Inactive 50%) 측면에서 비효율적이다.

Generational Algorithm

  • Copying Algorithm에서 Copy에 따른 Overhead 발생한다는 점, 대다수의 Object는 매우 수명이 짧다는 점, 소수의 수명이 긴 Object가 존재하고 이 Long Lived Obejct는 계속해서 복사된다는 점을 개선한 알고리즘
  • Heap 영역을 몇 개의 Sub Heap(Young Generation, Old Generation)으로 나누어 구성
  • Young Generation에서 자주 GC가 발생되고, 여기서 오래 살아남은 객체는 Old Generation으로 이동

    장점 & 단점

  • (+) 각 Sub Heap에 Mark and Sweep, Copying 등의 Algorithm을 가미할 수 있다.

Train Algorithm

  • TBD

    장점 & 단점

  • (+) TBD
  • (-) TBD

Hotspot JVM과 관련된 눈여겨 볼 점

Weak Generational Hypothesis

  • 대부분의 Obejct는 매우 수명이 짧다.
  • Old Object에서 Young Object로의 참조는 매우 적게 일어난다.

Young Generation - Speed

  • Fast Allocation & TLAB(Thread Local Allocation Buffer)
    • TLAB를 통해 Thread마다 할당할 수 있는 범위를 부여
    • Bump the Pointer 테크닉을 활용
    • Memory 할당의 효율성
    • TLAB 내에서는 동기화 이슈가 없음
    • Multi-thread 환경에서는 동기화 이슈가 발생
    • Allocation code 내에 포함되어 있으며 약 10개의 native instruction으로 구성

Old Generation - Efficiency

  • Card Table & Write Barrier
    • Old to Young reference를 구별하기 위해 사용
    • 1 Byte Card로 512 Byte의 Old generation 추적 가능
    • Write Barrier는 Bytecode Interpreter내에 포함되어 있으며 2 native instruction으로 구성

Garbage Collector의 종류

Serial Collector

Young Generation Algo' Old Generation Algo'
Generational Algo' Mark and Compacting
  • Hotspot JVM의 Default GC
  • Young 영역Eden / Survivor1 / Survivor2로 나누고 Eden 영역이 가득 찰 경우 Live Object만 Survivor1영역으로 보냄. 몇번의 반복 후 Survivor1영역이 가득차면 Live Object만 Survivor2로 보냄. 이후 Tenured 객체는 Old 영역으로 보냄
  • 관련 JVM 옵션 (이름만으로 유추 가능하므로 설명은..)
    • -XX:+UseSerialGC
    • -XX:MaxTenuringThreshold=<value>
    • -XX:+PrintTenuringDistribution

Parallel Collector

Young Generation Algo' Old Generation Algo'
Parallel Copy Mark and Compacting
  • 처리량(Throughput)을 개선하기 위한 GC
  • Young Gen'을 병렬처리해 처리량을 늘임. 방식은 Serial Collector의 Young gen과 동일하다.
  • 대용량 Memory, Multi core환경에서 처리량을 늘려 전체적인 수행속도 개선을 꾀함
  • GC를 Multithread로 수행
  • Large Young Generation의 경우 더욱 좋다.
  • Promotion Buffer 사용
    • PLAB(Parallel Local Allocation Buffer)라고도 불린다
    • Thread간 동기화 문제 회피 목적
    • Promotion을 위한 Thread별 공간이다
    • 반대급부로 Fragmentation이 발생 가능한데 이는 GC Thread를 감소시키거나 Old Generation Size를 증가시키면 어느정도 상쇄가능하다..
  • 관련 JVM 옵션 (이름만으로 유추 가능하므로 설명은..)
    • -XX:+UseParallelGC
    • -XX:ParallelGCThreads=<value_default:# of CPU>

Parallel Compacting Collector

Young Generation Algo' Old Generation Algo'
Parallel Copy Parallel Compacting
  • 기존 Parallel Collector에서 Old Gen'의 처리량도 늘이자는 목적
  • Multi CPU에서 유리하다.
  • Old Gen'의 Collection 시간을 줄여준다.
  • Old Generation은 다음과 같은 절차로 수행된다.
    • Mark
      • Multi Thread로 동작
      • STW 발생
      • Live Object를 마킹한 뒤, Generation을 몇 개의 Region으로 나누어놓는다.
    • Summary
      • Single Thread로 동작
      • Work Thread와 동시에 작업
      • 나뉘어진 Region 단위로 작업이 이루어진다.
      • 각 Region의 Density를 평가해 Dense Prefix를 설정한다.
      • Dense Prefix의 왼편에 있는 Region은 GC 대상에서 제외시킨다.
    • Compaction
      • Multi thread로 동작
      • STW 발생
      • Thread들이 각각 Region을 Collecting 한다.
      • Region들을 논리적인 Source / Destination으로 구분하여, Source의 Live Object들을 Destination의 빈 공간으로 이동시킨다.
  • 관련 JVM 옵션 (이름만으로 유추 가능하므로 설명은..)
    • -XX:+UseParallelOldGC
    • -XX:+UseParallelOldGCCompacting : Default True
    • -XX:+UseParallelDensePrefixUpdate : Default True

CMS Collector

Young Generation Algo' Old Generation Algo'
Parallel Copy Concurrent Mark and Sweep
  • 응답시간의 개선에 집중한 GC
  • Old Gen' 정리 시 최대한 STW 시간을 줄이고자 함(Low Pause GC)
  • Fast Elapsed Time에 중점을 둠
  • 빈번하지는 않지만 Old GC가 오래 수행되는 것을 개선
  • Low Latency Collector라고도 한다.
  • 대기시간을 분산해 응답시간을 개선하였다
  • 자원의 여유가 있는 상태에서 GC Pause Time을 줄이고자 할 때 적절하다.
  • Old Generation은 다음과 같은 절차로 수행된다.
    • Initial Mark
      • Single Thread로 동작한다.
      • STW 발생
      • Direct Referenced Object를 빠르게 식별한다.
    • Concurrent Mark
      • Single Thread로 동작한다.
      • Work Thread와 동시에 작업한다.
      • Direct Referenced Object에서 deep dive하며 참조되는 Object를 식별한다.
    • Remark
      • Multi thread로 동작한다.
      • STW가 발생한다.
      • Mark된 Object들을 재방문하여 Mark작업을 확정한다.
    • Concurrent Sweep
      • Single Thread로 동작한다.
      • Work Thread와 동시에 작업한다.
      • Dead Object Reclaim이 발생한다.
  • Compaction이 없어 시간절약할 수 있으나, 연속된 Free Space는 감소한다. 따라서 Allocation 시 FreeList를 탐색해 적절한 크기의 공간을 할당해야 한다. 이는 Fast Allocation의 장점을 없애버리고 Promotion과정에서 Young의 부담이 크다.
  • Concurrent Mark 중 새로운 Allocation이 발생가능한데, 이 Live Object가 바로 Garbage로 되어버리면 Floating Garbage가 발생한다. 이는 다음 GC 작업 때 reclaim되고, 이러한 현상이 빈번하면 Memory 요구량이 많아진다.
  • GC Scheduling
    • Old Gen이 가득차기 전에 작동한다.
    • Old Gen이 가득차기 까지 남은 예상 시간GC 수행에 필요한 시간과 근접해지면 작동한다.
    • Minor GC와 연달아 수행되지 않도록 Remark를 Minor GC 사이에 스케쥴링 한다.
    • 전체 Heap 점유율이 임계값(default:68%) 을 넘기면 작동한다.

Garbage First Collector

  • Train Algorithm의 아이디어를 상당부분 차용
  • Generation의 물리적인 구분이 사라짐
  • Heap을 여러개의 Region으로 나누고, 용도에 따라 Young, Old area로 간주함
  • Realtime에 가까운 Low pause 전략
  • Concurrent Marker가 각 Region 내 Live data의 양을 계산하여 Garbage가 많은 Region부터 GC를 시작한다.
  • G1GC는 다음과 같은 절차로 수행된다.
    • Young GC(Evacuation Pause)
      • Multi Thread로 동작
      • STW 발생
      • Live Object는 Survivor / Old Region으로 이동
    • Concurrent Mark - Marking
      • Single Thread로 동작
      • Work thread와 함께 작업
      • Evacuation Pause 때 넘어온 정보로 Initial Mark 수행
    • Concurrent Mark - Remarking
      • Multi Thread로 동작
      • STW 발생
      • Region당 Live Object를 계산하고 이 때 Empty Region은 즉시 reclaim한다.
    • Old Region Reclaim - Remark
      • Multi thread로 동작
      • Work thread와 함께 작업
      • Live Object가 적은 Region을 골라 몇 개의 Old Region만 collected
  • Free List를 사용하지 않고, TLAB를 사용함으로써 Fast Allocation을 지원한다.
  • Compaction 시 Linear 방식을 사용한다.
  • 관련 JVM 옵션 (이름만으로 유추 가능하므로 설명은..)
    • -XX:+UseG1GC
    • -XX:+UnlockExperimentalVMOptions

'개발언어 > JAVA' 카테고리의 다른 글

CMS 튜닝 (Java Optimization)  (0) 2021.03.30
Parallel GC 튜닝 (Java Optimization)  (0) 2021.03.28
GC 튜닝 (Java Optimization)  (0) 2021.03.22
GC Logging (Java Optimization)  (0) 2021.03.21
JVM miscellaneous (Java Optimization)  (0) 2021.03.21

@RestController 어노테이션

  • Spring에서 Rest API/Web API를 개발하기 위해 사용하는 어노테이션
  • @Controller@ResponseBody를 포함한다.
  • MessageConvertor가 @RestController에서 다음과 같은 중요한 역할을 수행한다.
    • 외부에서 전달받은 JSON메서드를 내부에서 사용할 수 있는 객체로 변환
    • Controller가 리턴한 객체를 클라이언트에게 JSON으로 변환해 전달
    • @EnableWebMvc를 사용하면 기본으로 제공된다. 이는 Jackson 라이브러리를 추가할 경우 기본으로 설정되어 있다. 만약 Jackson 라이브러리를 추가해 주지 않은 경우 500 오류가 발생한다.
    • @ResponseBody는 자바 객체를 HTTP 요청의 body 내용으로 매핑하는 역할을 수행하며, @RequestBody는 HTTP 요청의 body 내용을 자바 객체로 매핑하는 역할을 수행한다.
@RestController
public class MemberRestController
{
    @RequestMapping(value="api/item/new", method = RequestMethod.POST)
    public Item item(@RequestBody ItemDto item) {
        Item savedItem = itemService.join(item); 
        return savedItem;
    }
}
  • Body 부분의 ItemDto 데이터를 파라미터로 받기위해 @RequestBody를 사용함.
  • @ResponseBody@RestController에 포함되어있어, savedItem이 Body에 맵핑되어 전달된다.

'Backend > SpringBoot-JPA' 카테고리의 다른 글

2차 캐시 - @Cacheable  (0) 2021.04.30
Optimistic/Pessimistic Lock  (1) 2021.04.19
@Transactional  (0) 2021.03.21

트랜잭션 ACID 특성

  1. Atomicity
  • All or Nothing의 개념
  • 트랜잭션 중 Exception이 발생하면 해당 트랜잭션에서 진행한 모든 내용을 rollback 시켜야 한다.
  1. Consistency
  • 트랜잭션의 작업 처리 결과가 항상 일관성있어야 한다는 내용
  • 트랜잭션 진행 중 데이터가 변경되더라도 업데이트 된 내용으로 트랜잭션이 진행되는것이 아니라, 처음 트랜잭션을 진행할 때 참조한 데이터로 트랜잭션을 진행한다.
  1. Isolation
  • 둘 이상의 트랜잭션이 동시에 수행될 때, 한 트랜잭션의 수행이 트랜잭션에 영향을 주어서는 안된다.
  1. Durability
  • 트랜잭션이 성공적으로 완료되었을 때, 그 트랜잭션의 결과는 영구적으로 반영되어야 한다.

'Backend > 기타' 카테고리의 다른 글

REST API 와 Web API  (0) 2021.04.29

REST API란?

  • REST는 Representational State Transfer의 약자이고, REST AIP는 이러한 REST 형식의 API를 의미한다.
  • REST를 만족하기위한 요구조건은 다음과 같다.
    • Client-Server : HTTP를 이용함으로써 만족함
    • Stateless : HTTP를 이용함으로써 만족함
    • Cache : HTTP를 이용함으로써 만족함
    • Uniform interface
      • Resource는 URI로 식별되어야 한다.
      • Resource를 생성, 수정, 추가 시 HTTP 메시지에 표현을 해 전송해야한다.
      • 메시지는 Self-Descriptive 해야한다. JSON 메시지가 어디에 전달되는지, 이 메시지의 구성요소가 어떤 의미를 표현해야 Self-Descriptive라 부른다.
      • 애플리케이션의 상태는 Hyperlink를 이용해 전이되어야 한다.(HATEOAS) <a>태그와 같이 웹 페이지에서 제공하고 있으나 이를 API에서 제공하기는 쉽지않다.
    • Layer system : HTTP를 이용함으로써 만족함
    • Code on Demand : HTTP를 이용함으로써 만족함
  • REST의 요구조건 중 Uniform interface를 지원하는 것이 쉽지 않아 서비스에서 제공하는 API들을 Web API(HTTP API)라고 부르기도 한다.

Web API란?

  • URI는 정보의 자원을 표현해야 한다.
    • GET /members
  • 자원에 대한 행위는 HTTP Method(GET, POST, PUT, DELETE)로 표현한다.
  • 슬래시(/)는 계층을 나타낼 때 사용한다.
    • http://jay.com/member/1/items
    • 마지막 /는 포함하지 않는다.
    • 하이픈(``)은 URI의 가독성을 높이고자 할 때 사용한다.
    • 언더스코어(_)는 사용하지 않는다.
    • URI 경로는 소문자만을 사용한다.
    • 파일 확장자는 포함하지 않는다.

'Backend > 기타' 카테고리의 다른 글

트랜잭션의 ACID  (0) 2021.04.29

LOCK

OPTIMISTIC LOCK

  • 트랜잭션의 대부분은 충돌이 발생하지 않는다는 것을 가정하고 접근하는 방법으로 트랜잭션이 커밋하기 전까지는 충돌 여부를 알 수 없다.
  • JPA에서 OPTIMISTIC LOCK을 @Version을 사용하므로 Entity 정의 시 @Version이 명시된 필드를 추가하도록 하자. 또한, @Version이 정의되어 있다면 OPTIMISTIC LOCK을 사용할 것을 명시하지 않더라도 DEFAULT 적용 된다.
  • 이러한 OPTIMISTIC LOCK은 COMMIT 시점에 충돌을 알 수 있으며 충돌 시 다음과 같은 Exception이 발생한다.
    • javax.persistence.OptimisticLockException in JPA
    • org.hibernate.StaleObjectStateException in Hibernate
    • org.springframework.orm.ObjectOptimisticLockingFailureException in Spring

@Version

  • OPTIMISTIC LOCK 사용 시 @Version 어노테이션을 이용해 버전관리 기능을 추가해야 한다. 이는 Long, Integer, Short, Timestamp 타입에 적용 가능하다.
    • @Version
      private Long version;
  • 엔티티를 수정할 때 마다 버전이 하나씩 자동으로 증가하며, 수정 시 조회 시점의 버전과 수정 시점의 버전이 다르면 예외를 발생시킨다. 이를 통해 second lost update problem최초 커밋만 인정함으로써 방지할 수 있다. 다른 방법으로는 마지막 커밋만 인정, 충돌 내용 병합이 있으나 여기서 다룰 문제는 아니다.
    • tx-1> Member1 조회 (version 1)
    • tx-2> Member1 조회 (version 1)
    • tx-2> Member1 수정 (version 1->2)
    • tx-1> Member1 수정 (충돌 발생)
  • 엔티티 수정 시 다음과 같은 UPDATE 쿼리가 발생된다.
    • UPDATE MEMBER
      SET
         NAME=${NEW_NAME},
         VERSION=${OLD_VERSION + 1} 
      WHERE
         ID=${ID}
         AND VERSION=${OLD_VERSION}
    • 이러한 버전관리 필드는 JPA가 직접 관리하는데, 벌크 연산시에는 개발자가 관리해 주어야 한다.
    • UPDATE MEMBER m SET m.name = ${NEW_NAME}, m.version = m.version + 1

PESSIMISTIC LOCK

  • 트랜잭션의 충돌이 발생한다고 가정하고 우선 LOCK을 걸고 보는 방법이다.
  • SELECT FOR UPDATE, `` 등의 방법이 있다.
  • 데이터를 수정하는 즉시 충돌여부를 확인할 수 있으며, 다음과 같은 Exception이 발생한다.
    • javax.persistence.PessimisticLockException in JPA
    • org.springframework.dao.PessimisticLockingFailureException in Spring
  • PESSIMISTIC LOCK을 얻은 트랜잭션이 LOCK을 풀기 전까지 다른 트랜잭션들은 무한히 대기하게 되므로 다음과같이 timeout을 설정하는 것이 좋다.
    • Map<String, Object> properties = new HashMap<String, Object>();
      // timeout for 10 seconds
      properties.put("javax.persistence.lock.timeout", 10 * 1000);
      
      Member member = em.find(Member.class, id, LockModeType.PESSIMISTIC_WRITE, properties);
  • PessimisticLockScope를 통해 해당 Entity만 Lock을 걸지, 연관관계에 있는 Entity들도 Lock을 걸지 결정할 수 있다.
    • PessimisticLockScope.NORMAL: 해당 Entity만 Lock을 건다.
    • PessimisticLockScope.EXTENDED: 연관된 Entity들도 Lock이 걸린다.

JPA Lock

  • JPA 사용 시, READ COMMITTED + OPTIMISTIC LOCK을 조합하여 사용하는 것이 좋다.
  • Lock을 사용하는 대표적인 예는
    • Entity Manager: Member foundMember = em.find(Member.class, id, LockModeType.OPTIMISTIC);, em.lock(foundMember, LockModeType.OPTIMISTIC);, em.refresh()
    • Query: em.createQuery("select ~").setLockMode(LockModeTpye.OPTIMISTIC_INCREMENT);
    • @NamedQuery(name="lock", query="SELECT ~~", lockMode = OPTIMISTIC_INCREMENT)
      가 있다.

LockModeType의 종류

Lock Mode Type 설명
OPTIMISTIC LOCK OPTIMISTIC - OPTIMISTIC LOCK을 사용한다.
- @Version만 적용했을때에는 Entity 수정 시에만 버전을 확인하지만, 여기에서는 조회만 하여도 버전을 체크함으로써 트랜잭션이 종료될 때 까지 다른 트랜잭션에서 변경하지 않음을 보장해준다.
- 트랜잭션 커밋 시 버전정보를 조회(SELECT)해 Entity의 버전과 같은지 확인한다.
- DIRTY READ, NON-REPEATABLE READ를 방지한다.
OPTIMISTIC LOCK OPTIMISTIC_FORCE_INCREMENT - OPTIMISTIC LOCK과 함께 버전정보를 강제로 증가한다.
- Entity를 수정하지 않아도 트랜잭션 커밋 시 버전정보를 강제로 증가시킨다. 이 때, 조회시점의 버전과 다르면 Exception을 발생시킨다.
- Entity 수정 시 버전 UPDATE가 발생하는데, 트랜잭션 커밋 시점에도 버전정보가 증가하므로 2번의 버전 증가가 발생할 수 있다.
- 연관관계가 있는 Entity들에 대해 연관관계의 주인인 Entity의 변경이 발생하면 연관관계에 있는 Entity의 버전정보도 동일하게 변경된다. (Member 변경 시 Team 또한 Version이 같이 업데이트)
PESSIMISTIC LOCK PESSIMISTIC_READ - PESSIMISTIC LOCK, READ LOCK을 사용한다.
- 데이터를 읽기만하고 수정하지 않는 용도로 사용한다.
- shared lock으로 select ... for share와 같다.
PESSIMISTIC LOCK PESSIMISTIC_WRITE - PESSIMISTIC LOCK, WRITE LOCK을 사용한다.
- Lock이 걸린 row는 다른 트랜잭션이 수정할 수 없다.
- 내부적으로 select ... for update를 사용해 lock을 건다.
- exclusive lock
PESSIMISTIC LOCK PESSIMISTIC_FORCE_INCREMENT - PESSIMISTIC LOCK, 버전정보를 강제로 증가한다.
- PESSIMISTIC LOCK 중 유일하게 버전정보를 사용한다.
- for update nowait을 사용한다.
기타 NONE - LOCK을 사용하지 않는다.
- @Version필드가 있으면 OPTIMISTIC LOCK이 적용된다.
- Entity 수정 시, 버전 필드를 체크하면서 버전을 증가시킨다.

'Backend > SpringBoot-JPA' 카테고리의 다른 글

2차 캐시 - @Cacheable  (0) 2021.04.30
@RestController 어노테이션  (0) 2021.04.29
@Transactional  (0) 2021.03.21

CMS 튜닝

: CMS는 튜닝이 매우 까다로운 GC이며, CMS 플래그의 가짓수는 방대하므로 다음의 안티 패턴의 늪에 빠지지 않도록 유의하며 사용해야한다.

  • 스위치 만지작거리기
  • 민간 튜닝
  • 숲을 보지 못하고 나무만 보는 형태
  1. 처리율(Throughput) 관련 튜닝을 고려할 때, CMS 수집이 일어나면 코어의 절반은 GC에 할당되므로 애플리케이션의 처리율은 그만큼 절반이 날아간다. 이 때, CMF(Concurrent Mode Failure) 직전의 수집기 상태를 살펴보는 것은 중요한 지표를 가져다 준다. CMS 직후 새로운 CMS가 시작되는 Back-To-Back 현상은 CMS가 얼마 못 가 고장날거라는 신호이다. 애플리케이션 메모리 할당 속도가 회수 속도를 능가하며 CMF가 발생된다.

    이러한 Back-To-Back 현상은 전체 애플리케이션의 실행 처리율을 50%나 떨어뜨린다. 성능 엔지니어는 튜닝 시 이런 상황이 발생해도 괜찮은지 고민해보고, 괜찮지 않다면 코어 수를 늘리는 해결 방안을 모색해야 할 것이다.

    • CMS 중 GC에 할당된 코어 수는 다음의 -XX:ConGCThreads=<n> 플래그로 설정할 수 있다.
    • 디폴트 설정 상태에서 GC 스레드 수를 줄인다면, 부하 급등 시 애플리케이션의 회복력이 떨어져 CMF에 취약해지는 상황을 초래할 수 있음에 유의하자.
  2. CMS에서 STW는 다음의 두 단계,

    • Initial Mark: GC 루트가 직접 가리키는 내부 노드를 마킹한다.
    • Remark: 카드 테이블을 이용해 조정 작업이 필요한 객체를 식별한다.

    에서 이루어 진다. 따라서 CMS가 한 번 일어날 때마다 애플리케이션은 반드시 2번 멈추게 되는데, 세이프포인트에 예민한 저지연 애플리케이션에서는 중요한 영향을 미치게 된다.

    다음의 두 플래그를 동시 적용하자.

    • -XX:CMSInitiatingOccupancyFraction=<n>: CMS 초기 점유율로, CMS가 언제 수집을 시작할지 설정하는 플래그이다. 여기서 지정된 크기 (Default:75%)의 힙 영역이 찼을 때 최초의 CMS가 시작된다.
      (참고) CMS가 실행되면 영 수집을 통해 올드 영역으로 승격되는 객체들을 수용할 공간이 필요하다.
    • -XX:+UseCMSInitiatingOccupancyOnly: 이 플래그를 설정하면 초기 점유 공간을 동적 크기 조정하는 기능이 꺼지므로 위 플래그(CMSInitiatingOccupancyFraction)를 적용하지 않는다면 함부로 켜지 말아야 한다. 할당률이 심하게 튀는 CMS 애플리케이션의 경우 CMSInitiatingOccupancyFraction 매개변수 값을 줄임으로써 여유 공간을 늘이고, 능동적 크기 조정(adaptive sizing)기능을 끄는 전략을 구사함으로써 CMS GC를 자주 일으키더라도 CMF를 줄일 수 있을 것이다.

단편화로 인한 CMF

: CMS가 관리하는 프리 리스트로 인해 힙 단편화로 인한 CMF 발생 정보를 예측하는 방법이 존재한다.
: 다음의 플래그(-XX:PrintFLSStatistics=1)를 추가하면 GC 로그에 다음의 정보가 추가적으로 표기된다.

Total Free Space: xxx
(총 Free 공간)

Max Chunk Size: xxx
(최대 청크 크기)

Number of Blocks: xxx
(블록 갯수)

Av. Block Size: xxx
(평균 블록 크기)

Tree Height: xx
(트리 높이)

: 평균 블록 크기최대 청크 크기를 보고 메모리 청크의 크기 분포를 대략 짐작 할 수 있다.

또한, 크기가 큰 Live 객체Tenured 영역으로 옮길 때 그만한 크기의 청크가 바닥난 경우, GC Promotion이 악화되어 이는 CMF로 이어질 것이다. 그러면 JVM은 Parallel GC로 돌아가 Heap을 압착하고 프리 리스트를 병합하며 STW 시간은 길어진다. 이러한 정보들은 긴 STW이 임박했음을 미리 알수 있고, 센섬같은 툴을 이용하면 CMF에 근접했다는 사실을 자동 감지할 수 있다.

Parallel GC 튜닝

: 이 수집기의 특징은 다음의 항목들이다.

  • Full STW(Stop the world)
  • GC 처리율이 높고 계산 비용이 싸다.
  • 부분 수집이 일어날 가능성은 없다.
  • 중단 시간은 힙 크기에 비례하여 늘어난다.

위와 같은 특성들로 힙의 크기가 4GB 이하인 경우 Parallel GC가 아주 효과적이다.


GC 힙 크기 조정 Flag들은 다음과 같다. (과거에는 주로 이 플래그를 직접 적용하곤 했다고 한다. 요즘은 애플리케이션이 알아서 잘 결정하므로 명시적으로 크기를 설정하는 일은 잘 없다.)

플래그 설명
-XX:NewRation=<n> Young Generation/전체 힙 의 비율
-XX:SurvivorRatio=<n> Survivor / Young의 비율
-XX:NewSize=<n> 최소 Young Generation 크기
-XX:MaxNewSize=<n> 최대 Young Generation 크기
-XX:MinHeapFreeRation 팽창을 막기 위한 GC 이후 최소 힙 여유공간 비율
Default: 40%
힙 여유 공간이 정해진 수치(40%) 이하로 떨어지면 이 수치를 40%로 맞추기 위해 힙의 크기가 팽창된다.
-XX:MaxHeapFreeRatio 수축을 막기 위한 GC 이후 최대 힙 여유 공간 비율
Default: 70%
힙 여유 공간이 70% 이상으로 늘어나면 정해진 수치(70%)로 맞추기 위해 힙이 수축된다.

+ Recent posts