락 전략 with Laravel
2024. 10. 21. 11:53ㆍPHP
1. 낙관적 락 (Optimistic Lock)
개요
낙관적 락은 데이터 충돌이 거의 발생하지 않을 것이라는 가정하에 동작합니다. 실제로 데이터를 수정할 때만 충돌을 확인합니다.
사용 상황
- 읽기 작업이 쓰기 작업보다 훨씬 많은 경우
- 동시 수정 가능성이 낮은 경우
- 높은 동시성이 요구되는 경우
라라벨에서의 구현
라라벨에는 version 컬럼을 사용하여 낙관적 락을 구현합니다.
use Illuminate\Database\Eloquent\Model;
class Product extends Model
{
use \Illuminate\Database\Eloquent\Concerns\HasAttributes;
protected $fillable = ['name', 'price', 'version'];
}
// 사용 예시
$product = Product::find(1);
$product->price = 100;
$product->save();
if ($product->wasChanged()) {
// 성공적으로 업데이트됨
} else {
// 다른 프로세스가 이미 수정함
}
장점
- 높은 동시성: 락을 획득하기 위해 대기하지 않음
- 데드락 위험이 없음
- 데이터베이스 리소스 사용이 적음
단점
- 충돌 발생 시 애플리케이션에서 재시도 로직을 구현해야 함
- 높은 충돌 가능성이 있는 환경에서는 성능이 저하될 수 있음
1. 비관적 락 (Pessimistic Lock)
개요
비관적 락은 데이터 충돌이 자주 발생할 것이라고 가정하고, 데이터를 읽는 시점에 락을 겁니다.
사용 상황
- 동시 수정 가능성이 높은 경우
- 데이터 일관성이 매우 중요한 경우
- 트랜잭션이 짧고 빨리 처리되는 경우
라라벨에서의 구현
라라벨에서는 lockForUpdate() 메소드를 사용하여 비관적 락을 구현합니다.
use Illuminate\Support\Facades\DB;
DB::transaction(function () {
$product = Product::lockForUpdate()->find(1);
$product->price = 100;
$product->save();
});
장점
- 데이터 일관성 보장
- 충돌 처리 로직이 단순함
단점
- 동시성 저하: 다른 트랜잭션이 락이 해제될 때까지 대기
- 데드락 발생 가능성
- 데이터베이스 리소스 사용량 증가
3. 명시적 락 (Explicit Locking)
개요
명시적 락은 개발자가 직접 락을 제어하는 방식입니다. 라라벨에서는 Cache나 Redis를 사용하여 구현할 수 있습니다.
사용 상황
- 세밀한 락 제어가 필요한 경우
- 분산 환경에서의 동기화가 필요한 경우
- 데이터베이스 외부의 리소스에 대한 락이 필요한 경우
라라벨에서의 구현
라라벨의 Cache 파사드를 사용하여 구현할 수 있습니다.
use Illuminate\Support\Facades\Cache;
if (Cache::lock('product-1-lock')->get()) {
// 락 획득 성공, 작업 수행
$product = Product::find(1);
$product->price = 100;
$product->save();
Cache::lock('product-1-lock')->release();
} else {
// 락 획득 실패
}
장점
- 세밀한 제어 가능
- 데이터베이스에 의존하지 않는 락 구현 가능
- 분산 환경에서 효과적
단점
- 구현 복잡도 증가
- 락 해제 실패 시 문제 발생 가능
- 성능 오버헤드 발생 가능
데드락 (Deadlock) 해결 방법
데드락은 두 개 이상의 트랜잭션이 서로가 점유한 자원을 요구하며 무한정 대기하는 상황을 말합니다.
1. 타임아웃 설정
DB::transaction(function () {
// 트랜잭션 내용
}, 5); // 5초 타임아웃
2. 락 범위 최소화
필요한 최소한의 데이터에만 락을 적용합니다.
4. 재시도 로직 구현
use Illuminate\Database\QueryException;
$maxAttempts = 5;
$attempts = 0;
while ($attempts < $maxAttempts) {
try {
DB::transaction(function () {
// 트랜잭션 내용
});
break; // 성공 시 루프 탈출
} catch (QueryException $e) {
if ($e->getCode() == 40001) { // 데드락 에러 코드
$attempts++;
sleep(1); // 잠시 대기 후 재시도
} else {
throw $e; // 다른 에러는 그대로 던짐
}
}
}
if ($attempts == $maxAttempts) {
// 최대 시도 횟수 초과
}
'PHP' 카테고리의 다른 글
[Laravel] beginTransaction, lockForUpdate 활용 (일관성, 동시성) with. ORM (0) | 2024.08.12 |
---|---|
DTO DAO 정리 with 라라벨 (0) | 2023.04.21 |
라라벨 서비스컨테이너와 서비스프로바이더 with DIP (0) | 2023.03.17 |
라라벨 가바지 컬렉션 처리 (0) | 2023.03.17 |
Eloquent ORM 이란 (0) | 2022.12.14 |