
데이터베이스에서 락은 동시성 제어를 위해 사용됩니다. 동시에 여러 트랜잭션이 하나의 데이터에 접근할 때, 데이터의 일관성과 무결성을 유지하기 위해 도입된 메커니즘입니다. 락을 통해 특정 데이터나 자원에 대한 접근을 제어하고, 동시에 발생할 수 있는 충돌을 방지합니다.
@Entity
public class EntityWithOptimisticLock {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Version
private int version; // 버전을 통한 낙관적 락 구현
// 나머지 필드 및 메서드
}
@Repository
public interface EntityRepository extends JpaRepository<EntityWithPessimisticLock, Long> {
@Lock(LockModeType.PESSIMISTIC_WRITE)
@Query("SELECT e FROM EntityWithPessimisticLock e WHERE e.id = :id")
Optional<EntityWithPessimisticLock> findByIdForUpdate(@Param("id") Long id);
}
Redisson을 사용하여 분산 락을 구현하는 예제 코드입니다.
import org.redisson.api.RedissonClient;
import org.redisson.api.RLock;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.concurrent.TimeUnit;
@Service
public class DistributedLockService {
@Autowired
private RedissonClient redissonClient; // Redisson 클라이언트 자동 주입
public void executeCriticalTaskWithLock(Long taskId) {
RLock lock = redissonClient.getLock("taskLock:" + taskId); // 고유한 락 이름
try {
// 10초 내에 락 획득 시도, 최대 60초 동안 락 유지
boolean isLocked = lock.tryLock(10, 60, TimeUnit.SECONDS);
if (isLocked) {
// 여기에서 중요한 작업 실행
// 예: 데이터베이스 업데이트, 파일 작업 등
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
} finally {
if (lock.isHeldByCurrentThread()) {
lock.unlock(); // 락 해제
}
}
}
}
이 코드는 Redisson을 사용하여 분산 락을 구현한 예제입니다. RedissonClient를 통해 특정 자원("taskLock:" + taskId)에 대한 락을 얻으려고 시도하며, 락을 성공적으로 획득하면 중요한 작업(예: 데이터베이스 업데이트 등)을 수행한 후에 락을 해제합니다. tryLock 메소드는 주어진 시간 내에 락을 획득하려고 시도하며, 지정된 시간이 초과되면 실패합니다. 이 방식은 여러 인스턴스가 동시에 같은 자원에 접근하려고 할 때 데이터 일관성을 유지하는 데 도움이 됩니다.
분산 시스템을 다룰 때 분산 락은 필수적인 요소 중 하나입니다. Redisson 외에도 ZooKeeper, Etcd 등 다른 도구를 사용한 분산 락 구현도 가능합니다. 선택하는 도구는 애플리케이션의 요구 사항과 기술 스택에 따라 달라질 수 있습니다.