MySQL의 네임드 락(named lock)은 이름을 가진 사용자 정의 락으로, 특정 자원이나 작업의 동시 접근을 제어하기 위해 사용됩니다. 일반적인 테이블 락이나 행 락과는 달리, 네임드 락은 이름을 기준으로 동기화를 제어하며, 트랜잭션 단위 외부에서 유연하게 락을 걸고 해제할 수 있는 특징이 있습니다.
-- 'my_lock'이라는 이름으로 락을 요청하고, 10초 동안 기다립니다.
SELECT GET_LOCK('my_lock', 10);
-- 락을 얻으면 1을 반환, 대기 시간 초과로 실패 시 0을 반환합니다.
-- 락 해제
SELECT RELEASE_LOCK('my_lock');
- GET_LOCK(name, timeout): 락을 요청합니다. name은 락 이름을 나타내고 timeout은 락을 얻기 위해 대기할 시간을 초 단위로 지정합니다.
- 1: 락을 성공적으로 획득한 경우
- 0: 락을 획득하지 못한 경우 (예: 이미 다른 세션에서 해당 락을 보유 중인 경우)
- NULL: 오류가 발생한 경우 (예: 권한 문제나 기타 오류)
- RELEASE_LOCK(name): 해당 이름의 락을 해제합니다. 락이 성공적으로 해제되면 1, 요청자가 해당 락을 보유하고 있지 않으면 0, 오류가 발생하면 NULL을 반환합니다.
네임드 락의 주요 기능
- 임의의 이름 사용: 락 이름을 문자열로 지정하여 GET_LOCK() 함수로 락을 설정하고 RELEASE_LOCK() 함수로 해제할 수 있습니다. 이 락은 테이블이나 특정 레코드와는 관련이 없고, 단순히 이름만으로 관리됩니다.
- 세션 기반 락: 락은 설정한 세션에서만 해제할 수 있습니다. 세션이 종료되면 해당 세션에서 설정한 모든 네임드 락도 자동으로 해제됩니다.
- 트랜잭션 외부에서의 사용: 트랜잭션과 별도로 작동하므로, 트랜잭션 경계 외부에서도 락을 걸어 작업 동기화가 가능합니다. 네임드 락은 세션 기반으로 작동하므로 트랜잭션의 시작과 종료에 영향을 받지 않으며, 트랜잭션이 커밋되거나 롤백되더라도 유지됩니다. 이를 통해, 트랜잭션 경계에 관계없이 특정 자원에 대한 락을 설정하고 해제할 수 있습니다.
사용 예시
네임드 락_분산락은 주로 다음과 같은 상황에서 사용됩니다.
- 단일 자원에 대한 순차적 접근: 예를 들어, 특정 자원에 동시에 접근하면 안 되는 상황에서 충돌 방지를 위해 네임드 락을 사용합니다.
- 스케줄링 작업: 하나의 프로세스에서만 수행되어야 하는 주기적인 배치 작업에 적용하여 다른 프로세스에서 동일 작업을 수행하지 못하도록 할 때 유용합니다.
주의사항
- 네임드 락은 세션 단위로 적용되므로, 세션이 종료되면 자동으로 해제됩니다.
- 데이터베이스 락이므로, 자주 사용하게 되면 데이터베이스 부하가 높아질 수 있습니다.
- 락의 TTL(Time to Live)을 별도로 지정할 수 없으므로, 락을 영구적으로 점유하지 않도록 주의가 필요합니다.
트랜잭션 내에서의 네임드 락 동작 방식
- 트랜잭션 내부에서 락 획득 및 해제 가능: 트랜잭션 내부에서도 GET_LOCK()을 호출해 네임드 락을 설정할 수 있습니다. 이후 작업을 진행한 뒤 트랜잭션을 커밋하거나 롤백하더라도 네임드 락은 해제되지 않습니다.
- 락 유지: 트랜잭션이 종료되어도 네임드 락은 세션이 유지되는 한 계속 걸려 있습니다. 따라서 트랜잭션이 커밋이나 롤백으로 끝나도 락이 해제되지 않으며, 명시적으로 RELEASE_LOCK()을 호출하거나 세션이 종료되어야 해제됩니다.
- 트랜잭션 종료 후 작업 가능: 네임드 락은 트랜잭션과 독립적이기 때문에, 트랜잭션 종료 후에도 세션 내에서 계속 유효한 상태로 유지됩니다. 따라서 트랜잭션이 완료된 후 다른 작업을 수행할 때도 락을 보유할 수 있습니다.
-- 트랜잭션 시작
START TRANSACTION;
-- 'resource_lock'이라는 이름의 네임드 락 설정
SELECT GET_LOCK('resource_lock', 10);
-- 트랜잭션 내 작업 수행
UPDATE accounts SET balance = balance - 100 WHERE account_id = 1;
-- 트랜잭션 커밋 또는 롤백
COMMIT;
-- 네임드 락은 여전히 유지됨
-- 락 해제
SELECT RELEASE_LOCK('resource_lock');
API 요청과 MySQL 세션 종료
- API 요청의 생명주기: 일반적으로 각 API 요청은 DB와 연결을 생성하고 작업을 수행한 후 요청이 종료되면 세션도 함께 닫히는 방식입니다. 따라서, API 호출이 완료되면 MySQL 세션이 종료되고, 세션에 종속된 리소스(네임드 락 포함)도 자동으로 해제됩니다.
- 네임드 락의 해제: 만약 API 요청 중 네임드 락을 설정했다면, 요청이 끝나면서 세션이 종료될 때 자동으로 락도 해제됩니다. 즉, API 요청이 완료될 때마다 새 세션이 시작되고, 기존 세션이 닫히면서 네임드 락은 해제됩니다. 이를 통해 불필요한 락이 시스템에 남아 동시성 문제를 야기하지 않도록 합니다.
- 커넥션 풀링의 영향: 커넥션 풀링을 사용하는 경우, API 요청이 끝나도 세션이 종료되지 않고 풀에 반환되어 재사용될 수 있습니다. 이 경우에도, MySQL 네임드 락은 요청마다 명시적으로 해제하는 것이 좋습니다. 그렇지 않으면 동일한 커넥션을 재사용하는 다른 API 요청에서 락 충돌이 발생할 수 있습니다.
public ResponseEntity<String> processTask() {
try (Connection connection = dataSource.getConnection()) {
// 네임드 락 획득
Statement statement = connection.createStatement();
statement.execute("SELECT GET_LOCK('task_lock', 10)");
// 특정 작업 수행
performTask();
// 작업 완료 후 락 해제
statement.execute("SELECT RELEASE_LOCK('task_lock')");
return ResponseEntity.ok("Task completed");
} catch (Exception e) {
e.printStackTrace();
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body("Task failed");
}
}
하지만 분산 락의 경우 Redis를 추천
복잡한 분산 락이 필요한 경우 Redis와 같은 외부 시스템이 더 적합한 이유는 성능, 확장성, 안정성 측면에서 MySQL보다 우수하기 때문입니다.
- 성능과 응답 속도:
- Redis는 메모리 기반의 데이터 저장소로, 읽기/쓰기 속도가 매우 빠릅니다. 락 획득과 해제 시나 락 상태 확인 등의 작업이 신속하게 처리됩니다.
- MySQL은 트랜잭션과 관계형 데이터 관리에 최적화되어 있고 디스크 기반이기 때문에, 락을 빈번하게 사용하는 경우 MySQL에는 부하가 발생할 수 있으며 성능이 저하될 가능성이 큽니다.
- TTL(시간 초과 설정) 지원:
- Redis는 락에 TTL(Time To Live)을 설정할 수 있습니다. TTL은 락을 획득한 클라이언트가 비정상 종료되거나 네트워크 문제가 발생했을 때 락이 자동으로 해제될 수 있게 합니다. 이를 통해 잠금 상태가 영구적으로 유지되는 문제를 방지할 수 있습니다.
- MySQL의 네임드 락은 TTL을 기본 제공하지 않기 때문에, 락을 획득한 세션이 종료될 때까지 락이 지속될 수 있어 영구 락 문제를 수동으로 관리해야 합니다.
- 분산 환경에서의 락 관리:
- Redis는 여러 서버나 인스턴스가 동시에 락에 접근하는 분산 환경에서도 쉽게 락을 관리할 수 있습니다. Redis는 분산 락을 위한 Redlock 알고리즘을 통해 안정적인 락 제공을 지원합니다.
- 반면 MySQL 네임드 락은 기본적으로 단일 데이터베이스 인스턴스 내에서 작동하도록 설계되어 있어 다중 인스턴스나 분산 환경에서 락을 구현하는 데는 적합하지 않습니다.
- 락 상태의 유연한 관리:
- Redis는 여러 클라이언트나 시스템이 락의 상태를 쉽게 확인하고 제어할 수 있는 기능을 제공합니다. 예를 들어, Redis의 SETNX 명령어와 EXPIRE 옵션을 활용해 락의 생성과 동시에 시간 제한을 줄 수 있습니다.
- MySQL 네임드 락은 네임드 락의 상태를 쉽게 확인하거나 관리하기 어렵고, SQL 쿼리를 통해 제한적으로 확인하는 방법만 제공됩니다.
- 분산 트랜잭션 요구사항:
- Redis는 분산 트랜잭션을 지원하는 라이브러리와 결합하여 다양한 마이크로서비스 아키텍처에서 활용될 수 있으며, 락의 가용성과 일관성을 유지하는 데 최적화되어 있습니다.
- MySQL은 주로 단일 DB 내에서 트랜잭션을 관리하도록 설계되어 있어 분산 트랜잭션을 다루기 위한 락 시스템으로는 적합하지 않습니다.
Redlock 알고리즘의 동작 원리 for redis
Redlock은 일반적으로 5개의 Redis 노드(인스턴스)를 사용하여 구성됩니다. 락을 획득하기 위해 다음 절차를 수행합니다.
- 동시에 모든 Redis 인스턴스에 락을 요청:
- 클라이언트는 각 Redis 노드에 동일한 락(예: 고유한 UUID)을 설정하려고 시도합니다. 이때 SET resource_name lock_value NX PX 명령어를 사용해 락을 생성합니다.
- NX는 락이 존재하지 않을 때만 생성하도록 하고, PX는 TTL(타임아웃)을 설정해 락이 일정 시간 후에 자동으로 만료되도록 합니다.
- 과반수 이상의 인스턴스에서 락을 획득해야 함:
- 클라이언트는 5개의 노드 중 최소한 3개 이상의 노드에서 락을 획득해야 합니다.
- 또한 모든 락이 획득될 때까지 걸린 시간이 전체 TTL의 절반 이하(보통 설정한 TTL의 2/3 이하)여야 합니다. 이는 네트워크 지연으로 인해 TTL이 끝나기 전에 락을 잃어버릴 위험을 줄이기 위함입니다.
- 락 획득 성공 여부 판단:
- 과반수 이상의 노드에서 락을 지정된 TTL 내에 획득한 경우에만 락 획득에 성공한 것으로 간주하고 작업을 수행합니다.
- 그렇지 않으면 모든 노드에서 락을 해제하고 락 획득을 다시 시도합니다.
- 작업 완료 후 락 해제:
- 클라이언트는 작업을 완료한 후 모든 Redis 노드에서 락을 해제합니다.
- 락 해제는 각 Redis 노드에서 DEL 명령을 통해 수행되며, 락 획득 시 사용한 고유 ID를 사용해 해당 락을 정확히 해제합니다.
Redlock 알고리즘의 장점
- 안정성: 과반수 노드에서만 락을 유지하면 되므로, 일부 노드가 다운되거나 네트워크 지연이 발생해도 락을 유지할 수 있습니다.
- 고성능: TTL을 통해 락이 자동으로 해제되므로, 클라이언트가 비정상 종료되어도 시스템에 락이 영구적으로 걸리지 않습니다.
- 재진입 허용: 클라이언트가 동일한 락을 여러 번 획득할 수 있는 재진입을 허용하지 않음으로써 락의 일관성을 유지합니다.
Redlock 알고리즘의 사용 사례
Redlock은 특히 분산 시스템에서 여러 인스턴스나 프로세스가 동시에 접근하는 자원을 동기화할 때 사용됩니다. 예를 들어, 다중 서버 환경에서 하나의 자원을 동시에 수정하거나 처리하면 안 되는 경우나, 여러 노드에서 특정 자원에 대한 순차적 접근이 필요한 상황에서 유용하게 활용될 수 있습니다.
Redlock 알고리즘의 단점 및 주의점
일부 분산 시스템 전문가들은 Redlock이 네트워크 지연이나 Redis 서버의 TTL 동기화 문제 때문에 완벽한 일관성을 보장하지 못할 수 있다고 지적합니다. 따라서 중요한 트랜잭션 시스템에서는 Redlock을 단독으로 사용하기보다, 데이터의 일관성 요구 사항과 시스템의 특성을 고려하여 추가적인 안전 장치를 마련하는 것이 좋습니다.
과반수 락 획득?
Redlock 알고리즘에서 클라이언트가 5개의 노드 중 최소한 3개 이상의 노드에서 락을 획득해야 하는 이유는 분산 시스템의 일관성과 내결함성을 유지하기 위해서입니다. 구체적인 이유는 다음과 같습니다.
1. 과반수 규칙 (Majority Rule)
- 5개의 노드 중 3개의 노드에서 락을 획득하면 과반수를 확보하게 되므로, 일관성을 보장할 수 있습니다.
- 만약 클라이언트가 3개 이상의 노드에서 락을 획득하면 다른 클라이언트가 동시에 같은 이름의 락을 획득하는 상황을 피할 수 있습니다. 이로 인해, 시스템에서 같은 자원에 대해 두 개의 락이 생성되는 스플릿 브레인(split-brain) 문제를 예방할 수 있습니다.
2. 내결함성 (Fault Tolerance)
- Redlock에서는 노드 일부가 일시적으로 다운되더라도 시스템 전체가 정상적으로 동작할 수 있도록 설계되었습니다. 5개의 노드 중 2개가 다운되더라도 여전히 3개 이상의 노드에서 락을 획득할 수 있기 때문에 락의 가용성이 보장됩니다.
- 이로써 노드 장애나 네트워크 지연이 발생해도 여전히 락을 획득할 수 있는 가능성을 높이고, 잠금 상태의 지속성을 보장할 수 있습니다.
3. 데이터 일관성
- 락을 획득할 때 시간이 지남에 따라 노드별 TTL(락의 만료 시간)이 다르게 적용될 수 있습니다. 하지만 과반수에서 락을 획득함으로써 여러 클라이언트가 동일 자원을 동시에 접근하는 데이터 불일치 문제가 발생할 가능성을 줄일 수 있습니다.
- 만약 절반 미만의 노드에서만 락을 획득할 수 있다면, 이 락이 실제로 유효한지에 대한 신뢰성이 낮아져 다른 클라이언트가 중복으로 락을 획득할 가능성이 커지게 됩니다.
4. 분산 시스템에서의 합의 기반 접근
- 분산 환경에서 대부분의 분산 알고리즘은 과반수 합의를 통해 신뢰할 수 있는 결정(일관된 상태 유지)을 도출합니다. Redlock에서도 마찬가지로, 과반수 노드에서 락을 획득해야만 해당 락을 실제로 "획득했다"는 합의를 기반으로 동작하는 방식입니다.
- 이 접근 방식은 분산 시스템의 리더 선출이나 동기화 등의 문제에서 과반수 합의가 안전성과 일관성을 유지하는 기본 방법론임을 고려해 채택된 것입니다.
무슨 문제를 위해서 과반수락?
1. 중복 락 획득 (Duplicate Lock Acquisition)
- 정의: 중복 락 획득은 여러 클라이언트가 동시에 동일한 자원에 대해 락을 획득하려고 시도하는 상황을 의미합니다. 즉, 두 개 이상의 클라이언트가 동일한 락을 획득하게 되어 자원에 대한 무결성이 깨질 수 있는 상황입니다.
- 문제점:
- 자원에 대한 경쟁이 발생할 수 있으며, 두 클라이언트가 서로 다른 작업을 수행할 경우 데이터의 일관성이 무너질 수 있습니다.
- 예를 들어, 두 클라이언트가 동시에 동일한 파일에 데이터를 추가하거나 수정할 경우, 결과적으로 데이터가 손상되거나 원하지 않는 상태가 발생할 수 있습니다.
- 해결책: Redlock 알고리즘에서 과반수의 노드에서 락을 획득해야만 락을 실제로 사용할 수 있도록 함으로써, 중복 락 획득을 방지합니다. 이렇게 하면, 동시에 여러 클라이언트가 락을 획득하려 할 때, 과반수의 노드에서 락을 획득하지 못하면 다른 클라이언트가 해당 자원에 접근하지 못하도록 차단할 수 있습니다.
2. 스플릿 브레인 문제 (Split-Brain Problem)
- 정의: 스플릿 브레인 문제는 분산 시스템에서 네트워크 파티션이 발생하여 서로 다른 노드 그룹이 독립적으로 동작하게 되는 상황을 말합니다. 이로 인해 두 그룹이 각기 다른 결정이나 상태를 유지하게 되어 일관성이 무너지는 문제가 발생할 수 있습니다.
- 문제점:
- 예를 들어, 노드 A와 B가 서로 연결되어 있다가 네트워크 파티션으로 인해 C, D, E 노드와 연결이 끊어졌다고 가정해봅시다. C, D, E는 서로 독립적으로 동작하면서 락을 획득할 수 있으며, 이 과정에서 A와 B는 C, D, E의 상태를 알지 못합니다. 이로 인해 C와 D는 같은 자원에 대해 락을 획득하고 작업을 수행하게 됩니다.
- 결과적으로, A와 B가 네트워크 연결이 복구된 후, C와 D가 작업한 내용이 서로 충돌하거나 불일치하게 될 수 있습니다.
- 해결책: Redlock 알고리즘에서는 최소 3개 이상의 노드에서 락을 획득해야 한다고 요구합니다. 이렇게 함으로써 과반수의 노드에서 락을 획득해야만 자원을 사용할 수 있게 하여, 스플릿 브레인 상태에서도 하나의 리더를 정의하고 그에 따라 락을 적절히 관리할 수 있습니다. 네트워크 파티션이 발생하더라도, 과반수 노드의 결정에 따라 자원에 대한 접근을 조정할 수 있습니다.
근데 노드를 5개로 못하면?
1. 노드 수 감소에 따른 과반수 규칙
- 과반수 필요: Redlock에서 과반수의 노드에서 락을 획득해야 한다는 규칙은 일관성과 가용성을 보장하기 위해서입니다. 노드 수가 줄어들면 과반수를 정의하는 방법도 바뀌게 됩니다.
- 예를 들어, 5개의 노드 대신 3개의 노드를 사용할 경우, 과반수는 2개가 되므로 2개의 노드에서 락을 획득하면 락을 사용할 수 있습니다. 이 경우, 락을 획득하는 것이 더 용이해지지만, 노드 장애가 발생했을 때 시스템의 안정성이 떨어질 수 있습니다.
2. 내결함성 저하
- 장애 상황: 노드 수가 적어지면, 일부 노드가 다운되거나 네트워크 파티션이 발생했을 때 남은 노드들이 과반수를 형성하기 어려워질 수 있습니다. 예를 들어, 3개 노드 중 1개가 다운되면, 남은 2개에서만 락을 획득할 수 있으므로, 이 경우 락이 획득되지 않을 수 있습니다.
- 신뢰성: 더 적은 노드 수는 네트워크 오류 또는 노드 장애에 더 취약하게 만들어 전체 시스템의 신뢰성을 저하시킬 수 있습니다.
3. 데이터 일관성 문제
- 스플릿 브레인: 노드 수가 적으면 스플릿 브레인 문제를 더욱 쉽게 겪을 수 있습니다. 예를 들어, 2개의 노드만 있을 경우, 네트워크 파티션이 발생하면 두 노드가 서로 다른 상태를 유지하게 되어, 데이터 일관성 문제가 발생할 수 있습니다.
4. 3개 이상의 노드 사용 추천
- 3개 이상의 노드를 사용하는 것이 일반적으로 권장되며, 이를 통해 기본적인 가용성과 일관성을 확보할 수 있습니다. 3개 노드에서는 2개 이상에서 락을 획득해야 하며, 이로 인해 최소한의 내결함성과 데이터 일관성을 유지할 수 있습니다.
결론
Redlock 알고리즘은 원칙적으로 5개 노드를 사용하는 것을 권장하지만, 최소 3개 노드를 사용할 경우에도 기본적인 분산 락 기능을 수행할 수 있습니다. 그러나 노드 수가 적을수록 장애에 대한 취약성이 증가하므로, 시스템 설계 시 노드 수를 신중히 결정하는 것이 중요합니다.
'개발 > sql' 카테고리의 다른 글
비관락/낙관락 쓰기락/읽기락 베타락/공유락 (1) | 2024.11.09 |
---|---|
2 Phase Lock & mysql -> MVCC (3) | 2024.11.06 |
[p6spy] 설정 방법 (0) | 2024.10.21 |
[mysql] delete, drop, truncate (0) | 2024.10.02 |
[mysql] basic functions (0) | 2024.09.09 |