development

용어: 낙관적 동시성(Optimistic Concurrency)

주먹불끈 2025. 10. 1. 07:50

개요

도메인 주도 설계 첫걸음을 재독 중인데 명확히 하고픈 용어를 정리해둔다.

낙관적 동시성

1. 개념

“설마 충돌하겠어?”

낙관적 동시성(Optimistic Concurrency Control, OCC)은 동시 접근이 드물게 충돌한다고 가정하는 전략이다. 여러 사용자가 같은 데이터를 동시에 수정하더라도, 대부분의 경우 충돌이 일어나지 않는다는 “낙관적” 전제 위에서 동작한다.

즉, 먼저 락을 걸지 않고 자유롭게 작업을 진행한 뒤, 최종 저장 시점에 충돌 여부를 검사한다.

2. 동작 방식

  1. 읽기(Read): 데이터를 가져올 때 버전 정보(예: version number, timestamp)를 함께 가져온다.
    1. 이렇게 읽은 값에 대해 작업을 할 때까지 다른 사용자가 수정하지 않을 것이라고 낙관적으로 보는 것이다.
    2. 그러기 위해서 읽은 값이 수정되었는지 여부를 체크할 수 있는 값을 가져와둔다.
  2. 작업(Modify): 애플리케이션 레벨에서 데이터를 수정한다.
  3. 검증(Validate): 저장하려 할 때, 현재 DB의 버전과 처음 읽어온 버전을 비교한다.
    • 같다면 → 충돌 없음, 업데이트 수행
    • 다르다면 → 충돌 발생, 롤백 또는 재시도

3. 특징

장단점을 보고 낙관적 동시성을 어떤 도메인에 사용하면 좋을지를 생각해보자.

  • 장점
    • 락(lock)을 길게 잡지 않으므로 동시성 처리량이 높다.
    • 읽기 비중이 많은 시스템에 적합하다.
    • 단순한 구현으로도 데이터 무결성을 보장할 수 있다.
  • 단점
    • 충돌이 자주 발생하면 성능이 떨어진다.
    • 저장 실패 후 재시도 로직이 필요하다.

4. 예시 (Go + SQL)

// Product 테이블: id, name, stock, version
func UpdateStock(db *sql.DB, productID int, newStock int, version int) error {
    res, err := db.Exec(`
        UPDATE products
        SET stock = ?, version = version + 1
        WHERE id = ? AND version = ?
    `, newStock, productID, version)
    if err != nil {
        return err
    }

    rowsAffected, _ := res.RowsAffected()
    if rowsAffected == 0 {
        return errors.New("update conflict: data was modified by another transaction")
    }
    return nil
}
  • version 컬럼을 통해 동시성 충돌을 감지한다.
  • 업데이트하려는 시점에 버전이 다르면 다른 트랜잭션이 먼저 변경한 것이므로 실패 처리.

5. 활용 사례

  • 웹 애플리케이션: 사용자 프로필 수정 시, 최신 버전을 기반으로 업데이트
  • 이커머스: 주문 시 재고 수량 변경 시점의 충돌 방지
  • 협업 툴: 문서 동시 편집 충돌 감지

마무리

낙관적 동시성은 “락 대신 충돌 검증”으로 무결성을 지키는 방법이다.

읽기가 많고 쓰기 충돌이 드문 시스템에서는 효율적이지만, 충돌 빈도가 높다면 비관적 동시성(Pessimistic Concurrency)이 더 적합할 수 있다.

반응형