Backend/DB

[Oracle] 오라클 시퀀스가 동시성 이슈를 보장해주는 이유

Emil :) 2024. 6. 11. 17:44
728x90
반응형

서론


이전 회사에서 오라클 시퀀스를 활용해 선착순 이벤트 처리를 한 경험에 대해 복기하던 도중, 오라클 시퀀스가 어떻게 그걸 보장해주는지가 궁금해졌다.

오라클을 사용하지 않는다면 Redis를 사용해서 동시성 이슈를 제어하거나, 자바 단에서 thread, callback 등을 통해서 제어하는 방식이 있을텐데, 오라클에서 제공하는 기능을 활용해 훨씬 간단하게 업무를 처리한 기억이 있다.

아마 이런식으로 업무처리하신 분이 나 말고도 있을 것 같은데, 그게 어떻게 가능한건지 알아보도록 하자. 간단한 포스팅이니 이해가 쉬울듯.

시퀀스가 뭔지, 이런것들에 대한 내용은 다른 블로그를 참고하도록 하자.

참고


https://velog.io/@norighthere/Oracle-%EC%98%A4%EB%9D%BC%ED%81%B4-%EC%8B%9C%ED%80%80%EC%8A%A4Sequence-%EC%82%AC%EC%9A%A9%EB%B2%95

 

[Oracle] 오라클 시퀀스(Sequence) 사용법

시퀀스(Sequence)란 자동으로 순차적으로 증가하는 순번을 반환하는 데이터베이스 객체로 보통 PK값에 중복을 방지하기 위해 사용된다. 예를 들면, 사번의 번호가 20210041 로 주어진다고 했을 때 다

velog.io

https://docs.oracle.com/en/database/oracle/oracle-database/19/sqlrf/CREATE-SEQUENCE.html

 

SQL Language Reference

 

docs.oracle.com

https://dataonair.or.kr/db-tech-reference/d-lounge/expert-column/?mod=document&uid=53046

 

Oracle 데이터베이스로 보는 동시성 향상 비법

Oracle 데이터베이스로 보는 동시성 향상 비법 데이터베이스의 존재 이유 중 하나는 수많은 사용자가 동시에 데이터에 접근, 수정하고 입력하는 작업을 수행하는 환경을 제공하는 데 있다. 자고

dataonair.or.kr

본론


작업의 배경

기존 선착순 이벤트는 이러한 동시성 처리가 되어있지 않았다.

예를 들어 하루 40명까지만 당첨되는 선착순 이벤트인데, 39명까지 당첨된 상태에서 n명이 동시에 요청하면 n명이 모두 당첨되어버리는 이슈가 발생했었다. 코드로 보면 로직은 이런느낌으로 작성되어있었다.
실제 코드는 아니고, 스프링도 사용하지 않았어서.. 대충 느낌만 파악하자.

public void updateEvnetUser(Event event, User user) {
    // 선착순 이벤트에 당첨된 유저 가져오기
    int num = eventRepository.getEventUserCount(event, user);
    if(event.dailyPrizeCount() > num) {
    	eventRepository.savePrizedUser(event, user);
    } else {
    	// 선착순 당첨 끝이라는 문구 return
    }
}
이전에 작성한 kafa & redis 학습 내용에 첨부한 이미지, https://kkkapuq.tistory.com/154

이런식으로 코드가 작성되어 있어서 추가 발급이 되었던 것. 그래서 db에 저장할 때 다음과 같이 로직을 수정했었다.

public void updateEvnetUser(Event event, User user) {
    // 선착순 이벤트에 당첨된 유저를 row 수가 아닌 시퀀스로 가져오기
    int num = eventRepository.getEventUserSeq(event);
    if(event.dailyPrizeCount() > num) {
    	eventRepository.savePrizedUser(event, user);
    } else {
    	// 선착순 당첨 끝이라는 문구 return
    }
}

이런식으로 기존엔 당첨된 유저의 row 수를 기준으로 조회했는데, 시퀀스로 조회하게끔 변경했었음.

동작 원리

해당 작업을 할 때만 해도, 시퀀스 자체가 DB가 기본적으로 제공하는 기능인 줄 알았는데, MySQL은 그렇게 없더라. 그래서 좀 놀랐었다.
역시 비싼값하는구나 오라클

아무튼, 크게 3가지 특성을 활용해 동시성 처리가 가능하다. 트랜잭션 처리처럼 매우 밀접한 연관이 있다.

원자성

시퀀스는 테이블과 다르게 독립적으로 형성된다. 또한 오라클 자체적으로 원자적 연산을 통해 유니크한 값을 만들어낸다.

시퀀스를 형성하는 과정 자체가 하나의 트랜잭션으로 처리되기 때문에, 다른 트랜잭션이 중간에 개입할 수가 없다.

락 활용

시퀀스 생성 요청이 들어오면, 해당 시퀀스 객체에 락을 걸고, 추가적으로 들어오는 시퀀스 생성 요청이 접근할 수 없도록 막는다.

캐싱

이전에 캐싱된 번호에 +1 해서 시퀀스를 생성하기 때문에 동시성 이슈 방지가 가능하다.

시나리오로 설명을 해보자면,

  1. 초기 캐시 값이 할당된다. 여기서 최소값과 최대값을 결정한다.
  2. 캐시에서 번호를 가져온다. 캐시 번호가 남아있다면 해당 번호를 반환한다.
  3. 캐시 번호가 소진되면 새로운 캐시 범위를 가져와 재충전해준다.

이런식으로 시퀀스 캐싱 작업이 이루어진다. 

이게 전부다. 결국 시퀀스를 형성할 때는 트랜잭션을 Serializable로 걸어버려서 동시성 이슈를 원천 차단하는 것.
그리고 그 과정에서 성능을 고려해 락과 캐싱을 활용하는 것이다.

결론


결국 트랜잭션의 기본적인 원리로 동작하는 것이었다.

무슨 오라클만의 대단한 기술이 있는건줄 알았는데.. 큰 호기심을 갖고 알아봤는데 생각보다 별거아니라서 싱거운 느낌이다.

구독 및 하트는 정보 포스팅 제작에 큰 힘이됩니다♡

728x90
반응형