Pink Spider/데이터베이스에서 모든 트랜잭션에 대해 Lock과 Unlock을 사용하는 방법

Created Wed, 26 Mar 2025 11:33:45 +0900 Modified Mon, 08 Dec 2025 08:41:47 +0900
1126 Words 5 min

데이터베이스에서 모든 트랜잭션에 대해 LockUnlock을 사용하는 방법은 데이터베이스 및 트랜잭션의 동작 방식에 따라 다릅니다. 일반적으로 테이블 잠금, 행 잠금, 트랜잭션 격리 수준, 명시적 잠금 해제를 조합하여 구현할 수 있습니다. Java 환경에서는 JDBC, JPA, QueryDSL 등을 활용해 이를 설정할 수 있습니다.

아래는 SELECT뿐만 아니라 INSERT, UPDATE, DELETE 등 모든 트랜잭션에서 Lock과 Unlock을 사용하는 방법을 설명합니다.


1. 테이블 잠금 (Table Lock)

테이블 단위로 잠금을 설정하여 트랜잭션이 완료되기 전까지 다른 트랜잭션의 접근을 막습니다.

예제 (JDBC 기반):

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.Statement;

public class TableLockExample {
    public static void main(String[] args) {
        String url = "jdbc:mysql://localhost:3306/your_database";
        String user = "your_username";
        String password = "your_password";

        try (Connection connection = DriverManager.getConnection(url, user, password)) {
            // 트랜잭션 시작
            connection.setAutoCommit(false);

            // 테이블 잠금
            try (Statement statement = connection.createStatement()) {
                statement.execute("LOCK TABLES your_table WRITE");

                // INSERT, UPDATE, DELETE 수행
                statement.executeUpdate("INSERT INTO your_table (column1) VALUES ('value')");
                statement.executeUpdate("UPDATE your_table SET column1 = 'new_value' WHERE column2 = 'some_value'");
                statement.executeUpdate("DELETE FROM your_table WHERE column2 = 'some_value'");

                // 커밋 및 잠금 해제
                connection.commit();
                statement.execute("UNLOCK TABLES");
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

주요 포인트:

  • LOCK TABLES your_table WRITE: 테이블 전체에 쓰기 잠금 설정.
  • UNLOCK TABLES: 잠금 해제.

2. 행 잠금 (Row Lock)

특정 행에 대해 잠금을 설정하여 다른 트랜잭션이 동시에 해당 행을 수정하거나 삭제하지 못하도록 설정합니다.

예제 (JPA 기반):

import jakarta.persistence.LockModeType;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

@Service
public class RowLockExample {
    private final JPAQueryFactory queryFactory;

    public RowLockExample(EntityManager entityManager) {
        this.queryFactory = new JPAQueryFactory(entityManager);
    }

    @Transactional
    public void updateWithRowLock(Long id, String newValue) {
        QYourTable yourTable = QYourTable.yourTable;

        // 행 잠금 설정
        YourTable record = queryFactory.selectFrom(yourTable)
                .where(yourTable.id.eq(id))
                .setLockMode(LockModeType.PESSIMISTIC_WRITE)
                .fetchOne();

        if (record != null) {
            record.setColumn1(newValue);
            queryFactory.getEntityManager().merge(record);
        }
    }
}

주요 포인트:

  • LockModeType.PESSIMISTIC_WRITE: 특정 행을 잠그고 다른 트랜잭션의 쓰기 작업을 차단.

3. 트랜잭션 격리 수준

격리 수준을 설정하면 모든 트랜잭션에서 잠금 동작을 관리할 수 있습니다.

트랜잭션 격리 수준 설정:

@Service
public class IsolationExample {
    @Transactional(isolation = Isolation.SERIALIZABLE)
    public void performTransaction(Long id) {
        // 트랜잭션 내 작업
    }
}

Isolation 옵션:

  • READ_UNCOMMITTED: 데이터 잠금을 사용하지 않음.
  • READ_COMMITTED: 커밋된 데이터만 읽을 수 있음.
  • REPEATABLE_READ: 트랜잭션 내 동일 데이터가 변경되지 않음.
  • SERIALIZABLE: 트랜잭션을 순차적으로 실행하며, 가장 높은 격리 수준.

4. 명시적 Lock 및 Unlock

명시적으로 LOCKUNLOCK을 수행하여 트랜잭션 작업을 관리할 수 있습니다.

예제 (Native Query):

@Service
public class ExplicitLockExample {
    @PersistenceContext
    private EntityManager entityManager;

    @Transactional
    public void performTransactionWithLock() {
        // 명시적으로 테이블 잠금
        entityManager.createNativeQuery("LOCK TABLES your_table WRITE").executeUpdate();

        // 작업 수행
        entityManager.createNativeQuery("INSERT INTO your_table (column1) VALUES ('value')").executeUpdate();

        // 명시적으로 잠금 해제
        entityManager.createNativeQuery("UNLOCK TABLES").executeUpdate();
    }
}

5. QueryDSL 기반 잠금 처리

QueryDSL을 사용해 모든 트랜잭션에서 잠금을 관리할 수 있습니다.

QueryDSL에서 Lock 설정:

@Transactional
public void performQueryWithLock(Long id) {
    QYourTable yourTable = QYourTable.yourTable;

    // Pessimistic Lock 설정
    YourTable record = queryFactory.selectFrom(yourTable)
            .where(yourTable.id.eq(id))
            .setLockMode(LockModeType.PESSIMISTIC_WRITE)
            .fetchOne();

    if (record != null) {
        // 데이터 작업 수행
        record.setColumn1("new_value");
        queryFactory.getEntityManager().merge(record);
    }
}

6. Lock 적용 시 고려 사항

  • 잠금 범위 제한: 테이블 전체보다는 가능한 행 수준에서 잠금을 설정.
  • Deadlock 방지: 여러 트랜잭션이 교착 상태에 빠지지 않도록 잠금 순서를 설계.
  • 성능 고려: 높은 트랜잭션 격리 수준이나 광범위한 잠금은 성능에 영향을 줄 수 있음.
  • 트랜잭션 종료 시 잠금 해제: 트랜잭션 커밋 또는 롤백 시 자동으로 잠금 해제되도록 트랜잭션 관리에 유의.

요약

  • 테이블 잠금: LOCK TABLESUNLOCK TABLES.
  • 행 잠금: LockModeType.PESSIMISTIC_WRITE 또는 FOR UPDATE.
  • 트랜잭션 격리 수준: @Transactional(isolation = ...).
  • QueryDSL: setLockMode를 사용해 잠금 처리.
  • 명시적 잠금: Native Query로 제어.

상황에 맞는 잠금 전략을 선택하고 성능 및 동시성 문제를 최소화하도록 설계하세요.