동시성 문제 – 클론코딩 주차

복제 인코딩을 위한 히트 기능 추가

동시성 문제는 예상되는 문제입니다.

동시성 문제

여러 쓰레드가 동시에 같은 리소스에 접근하여 수정할 때 발생하는 문제

@Transactional
public OneBoardResponseDto getBoard(Long id) {
    Board board = boardRepository.findById(id).orElseThrow(()->new IllegalArgumentException("글이 존재하지 않습니다"));
    board.viewBoard();
    List<Job> jobList = jobRepository.findByBoard(board);
    List<OneJobResponseDto> jobResponse = new ArrayList<>();
    for (Job job : jobList) {
        jobResponse.add(new OneJobResponseDto(job));
    }
    Integer fav =favoritesRepository.countByBoard(board);
    OneBoardResponseDto boardResponse = new OneBoardResponseDto(board,jobResponse,fav);
    return boardResponse;
}
public void viewBoard(){this.viewCount++;}

@Transactional 어노테이션을 붙였기 때문에 동시성 문제가 없다고 생각했습니다.




조회수 39회

조회수가 0인 경우 여러 쓰레드가 상태 0을 읽고 1로 업데이트하므로 업데이트가 제대로 되지 않습니다.
이는 여러 스레드가 동시에 읽고 업데이트하기 때문에 발생합니다.

해결

1. 쿼리 사용

@Modifying
@Query("UPDATE Board b SET b.viewCount = b.viewCount + 1 WHERE b.id = :id")
void viewBoard(Long id);

이전에는 내부 방법을 사용하여 적중 수를 수집했지만 이번에는 쿼리를 직접 사용합니다.

같은 결과


조회수가 정확히 200으로 업데이트되었습니다.

사실 이것은 내 레벨 프로젝트에서 문제가 되지 않습니다.

2.블록 사용

암석의 종류

비관적 잠금: 비관적 잠금

낙관적 잠금: 낙관적 잠금

비관적 잠금:

– 일관성을 유지하기 위해 실제 데이터 잠금

– 배타적 잠금이 적용되면 다른 트랜잭션은 잠금이 해제될 때까지 데이터를 커밋할 수 없습니다.

-자원 요청에 의해 동시성 문제예상 잠금 방법

– Deadlock 이슈가 발생할 수 있으니 주의

*교착 상태: 요청이 잠겨 있으면 프로세스가 완료될 때까지 다른 요청이 데이터에 액세스할 수 없습니다.

– 롤백이 발생하더라도

요약: 잠금이 있는 스레드에 대한 액세스만 제어하는 ​​방법

낙관적 잠금 :

– 실제로 Lock을 사용하지 않고 버전을 사용하여 일관성 조정

– 먼저 데이터를 읽은 후 업데이트할 때만 내가 읽은 버전이 맞는지 확인하여 업데이트

– 리소스를 잠그지 않고 문제를 다루다방법

– 읽기 버전에서 변경이 있을 때 롤백 작업을 수행해야 합니다.

– 버전 정보를 저장해야 하므로 DB 용량 추가 증가 가능

– 데이터의 일부가 변경되더라도 전체 데이터의 버전 정보가 변경되므로 다른 트랜잭션에서 동시에 변경될 경우 충돌 가능성이 높습니다.

– 충돌 감지는 읽기에 대해서만 수행되므로 충돌 감지로 인해 높은 병렬 환경에서 성능 저하가 발생할 수 있습니다.

비관적 자물쇠로 이동

@Lock(LockModeType.PESSIMISTIC_WRITE)
@Query("SELECT b FROM Board b WHERE b.id = :id")
Board findAndLockById(Long id);

@Modifying
@Query("UPDATE Board b SET b.viewCount = b.viewCount + 1 WHERE b.id = :id")
void viewBoard(Long id);
@Transactional
public OneBoardResponseDto getBoard(Long id) {
    Board board = boardRepository.findAndLockById(id);//.orElseThrow(()->new IllegalArgumentException("글이 존재하지 않습니다"));
    boardRepository.viewBoard(id);
    List<Job> jobList = jobRepository.findByBoard(board);
    List<OneJobResponseDto> jobResponse = new ArrayList<>();
    for (Job job : jobList) {
        jobResponse.add(new OneJobResponseDto(job));
    }
    Integer fav =favoritesRepository.countByBoard(board);
    OneBoardResponseDto boardResponse = new OneBoardResponseDto(board,jobResponse,fav);
    return boardResponse;
}

비관적 잠금이 findBy에 배치된 다음 논리를 사용하여 쿼리를 사용하여 히트 수를 증가시켰습니다.

https://github.com/Go-Zik/BE-GO-ZIK

GitHub – 고직/BE-GO-ZIK

GitHub에서 계정을 생성하여 Go-Zik/BE-GO-ZIK의 발전에 기여하십시오.

github.com