홈 › SQL 실무 › 07 / 9

채번과 순번 관리

일자별 일련번호, 채번 테이블, 동시 요청
섹션 6진행 0 / 9

2. 핵심 원리

2.1 업무 번호의 구조

주문번호를 세 부분으로 나눠 생각하면 쉽습니다.

부분 예 만드는 방법
접두어 ORD- 고정 문자열
날짜 20260930 날짜를 8자리 문자로 변환
일련번호 0001 숫자를 4자리로 0 채움

앞의 두 부분은 값이 정해져 있어서 어렵지 않습니다. 문제는 일련번호입니다. 그날 지금까지 몇 번까지 나갔는지를 어디에 기록하고, 여러 요청이 동시에 와도 각자 다른 번호를 받게 만드는 것이 이 레슨의 주제입니다. 방법은 크게 세 가지입니다.

방식 번호를 어디서 얻나
MAX + 1 주문 표에서 그날 최댓값을 읽어 1 을 더함
채번 테이블 별도 표의 카운터를 UPDATE 로 올림
시퀀스 DB 가 관리하는 숫자 발급기

2.2 MAX + 1 이 깨지는 이유

MAX+1 은 읽고 나서 쓰는 두 단계입니다. 두 세션이 각각 MAX 를 읽으면 둘 다 같은 값을 보고, 같은 번호를 만들어 INSERT 합니다. 주문번호에 PK 나 UNIQUE 가 있으면 두 번째 INSERT 가 오류로 막힙니다. 제약이 없으면 같은 번호의 주문이 두 건 생깁니다.

그래서 MAX+1 을 쓸 때는 다음 두 가지를 함께 둡니다.

  • 번호 컬럼에 PK 나 UNIQUE 를 둔다.
  • 중복 오류가 나면 번호를 다시 구해 INSERT 를 재시도한다.

트래픽이 낮은 내부 시스템이라면 이것으로 충분한 경우도 많습니다. 동시 요청이 잦으면 재시도가 늘어 곤란해집니다.

핵심

MAX+1 은 읽기와 쓰기 사이에 틈이 있어서 동시 요청에 취약합니다. 최소한 PK 로 막고 재시도하거나, 채번 테이블로 바꿉니다.

2.3 채번 테이블과 UPDATE 먼저

채번 테이블은 업무 구분(biz)과 날짜(ymd)마다 행 하나를 두고, 그 행의 last_no 가 그날 마지막으로 발급한 번호입니다. 새 번호가 필요하면 그 행을 last_no = last_no + 1 로 UPDATE 하고 올라간 값을 읽습니다.

핵심은 순서입니다. UPDATE 는 실행하는 순간 그 행에 쓰기 잠금을 겁니다. 다른 세션이 같은 행을 UPDATE 하려 하면 첫 세션이 COMMIT 이나 ROLLBACK 을 할 때까지 기다립니다. 그래서 동시 요청이 줄을 서고, 각자 서로 다른 값을 받습니다. 읽기를 먼저 하지 않고 UPDATE 를 먼저 하는 것이 MAX+1 과의 차이입니다.

행 biz ymd last_no
1 ORD 20260929 2
2 ORD 20260930 0

그날 첫 요청이면 행이 아직 없습니다. 행이 없을 때만 INSERT 하고 있으면 건드리지 않는 처리가 필요하고, Oracle·MSSQL 은 MERGE, MySQL 은 INSERT IGNORE 를 씁니다. 두 세션이 동시에 행을 만들려 하면 한쪽이 PK 오류를 받으므로 그때는 UPDATE 부터 다시 시도합니다.

2.4 SELECT ... FOR UPDATE 방식

UPDATE 대신 먼저 행을 읽으면서 잠그는 방식도 있습니다. SELECT last_no ... FOR UPDATE 로 잠근 뒤 값에 1 을 더해 UPDATE 합니다. 순서가 정해지는 결과는 같지만 문장이 두 개로 늘어납니다.

DB 읽으면서 잠그기 한 문장으로
Oracle · Tibero SELECT ... FOR UPDATE UPDATE ... RETURNING last_no INTO :n
MySQL InnoDB SELECT ... FOR UPDATE LAST_INSERT_ID(last_no + 1) 로 UPDATE 후 조회
MSSQL FROM 뒤 WITH (UPDLOCK, HOLDLOCK) 힌트 UPDATE ... OUTPUT inserted.last_no

MSSQL 에는 FOR UPDATE 가 없습니다. 힌트는 BEGIN TRAN 안에서만 의미가 있고, 한 문장으로 끝내려면 OUTPUT 절이 더 간단합니다. Oracle 의 RETURNING INTO 와 MSSQL 의 OUTPUT 은 UPDATE 와 값 읽기 사이의 틈을 없애 줍니다.

2.5 시퀀스와 비교

시퀀스는 트랜잭션과 무관하게 빠르고 잠금을 잡지 않아 동시성에 가장 강합니다. 대신 날짜별로 1 부터 다시 시작하기 어렵고, 롤백해도 되돌아가지 않아 번호에 구멍이 생깁니다. 그래서 "날짜별로 1 부터, 가능하면 빈 번호 없이" 가 요구인 업무 번호에는 채번 테이블이 자주 쓰이고, 번호가 그냥 유일하기만 하면 되는 내부 키에는 시퀀스가 어울립니다.

항목 MAX + 1 채번 테이블 시퀀스
동시 요청 충돌, 재시도 필요 잠금으로 줄 섬 충돌 없음
일자별 1 부터 자연스러움 행을 날짜별로 둠 어려움
번호 구멍 마지막 행 삭제 시 번호 재사용 분리 커밋 시 발생 롤백해도 발생
병목 없음(재시도만) 채번 행 하나에 집중 거의 없음