분산 잠금 장치
1) 분산 잠금 장치가 필요한 이유 (및 언제)
분산 차단은 여러 클러스터 노드 간의 중요한 섹션에 대한 상호 배제를 보장하는 메커니즘입니다. 일반적인 작업:- 배경 작업/셰이더를위한 지도자 선거.
- 공유 리소스 (파일 이동, 스키마 마이그레이션, 독점 지불 단계) 에 대한 단일 수행자의 제한.
- 그렇지 않으면 demempotency/order를 달성 할 수없는 경우 집계 (지갑/주문) 의 순차적 처리.
- dempotent upsert, CAS (비교 및 세트) 또는 키 당 주문을 할 수있는 경우.
- 자원이 정류 작업을 허용하는 경우 (CRDT, 카운터).
- 한 상점의 트랜잭션으로 문제가 해결되면.
2) 위협 모델 및 속성
실패와 합병증:- 네트워크: 지연, 파티션, 패킷 손실.
- 프로세스: GC 일시 정지, 세계 정지, 잠금 캡처 후 충돌.
- 시간: 시계 드리프트 및 변위 중단 TTL 접근.
- 리포지션: 그물 뒤의 "좀비" 프로세스는 여전히 성을 소유하고 있다고 생각할 수 있습니다.
- 안전: 유효한 소유자 (안전) 를 넘지 않습니다.
- 생존 가능성: 소유자가 실패하면 자물쇠가 풀립니다 (활력).
- 정의: 금식은 없습니다.
- 시계 독립성: 정확성은 벽 시계에 의존하지 않습니다 (또는 펜싱 토큰으로 보상).
3) 메인 모델
3. 1 임대 (렌탈 락)
자물쇠는 TTL과 함께 발행됩니다. 소유자는 만료 전에 (심장 박동/유지) 갱신해야합니다.
장점: 자기 흡수 충돌.
위험: 소유자가 "고착" 되어 계속 작동하지만 확장을 잃은 경우 이중 소유권이 발생할 수 있습니다.
3. 2 펜싱 토큰
성공적인 캡처마다 단조롭게 증가하는 숫자가 발행됩니다. 리소스 소비자 (데이터베이스, 대기열, 파일 스토리지) 는 토큰을 확인하고 이전 번호로 작업을 거부합
이것은 TTL/임대 및 네트워크 파티션에 매우 중요합니다. "이전" 소유자로부터 보호합니다.
3. 정원 잠금 장치 3 개 (CP 시스템)
분산 컨센서스가 사용됩니다 (Raft/Paxos; etcd/ZooKeeper/Consul), 레코드는 합의 로그와 관련이 있습니다. → 대부분의 노드에는 분할 뇌가 없습니다.
또한 강력한 보안 보장.
마이너스: 쿼럼에 대한 민감도 (분실 될 때 생존 가능성은 절름발이).
3. 4 개의 AP 잠금 (메모리/캐시 + 복제)
예를 들어 Redis 클러스터입니다. 높은 가용성과 속도이지만 네트워크 파티션에 대한 강력한 보안 보장이 없습니다. 타박상 측면의 펜싱이 필요합니다.
4) 플랫폼 및 패턴
4. 1 etcd/ZooKeeper/Consul (강력한 자물쇠에 권장)
Ephemeral nodes (ZK) 또는 세션/리스 (etcd): 세션이 살아있는 동안 키가 존재합니다.
세션 기념품; 쿼럼 손실 → 세션이 만료됨에 따라 잠금 장치가 해제됩니다.
대기열 → 페어에 대한 시퀀스 노드 (ZK 'EPHEMERAL _ SEQUENTIAL').
go cli, _:= clientv3. New(...)
lease, _:= cli. Grant(ctx, 10) // 10s lease sess, _:= concurrency. NewSession(cli, concurrency. WithLease(lease. ID))
m:= concurrency. NewMutex(sess, "/locks/orders/42")
if err:= m. Lock(ctx); err!= nil { / handle / }
defer m. Unlock(ctx)
4. 2 리디스 (깔끔한)
클래식- 'SET 키 값 NX PX ttl'.
문제:- 복제/feilover는 동시 소유자를 허용 할 수 있습니다.
- 여러 인스턴스의 레드 락은 위험을 줄이지 만 제거하지는 않습니다. 신뢰할 수없는 네트워크가있는 환경에서는 논란의 여지가 있습
Redis를 빠른 조정 계층으로 사용하는 것이 더 안전하지만 항상 대상 리소스의 펜싱 토큰을 보완합니다.
예 (Lua 잠금 해제):lua
-- release only if value matches if redis. call("GET", KEYS[1]) == ARGV[1] then return redis. call("DEL", KEYS[1])
else return 0 end
4. DB 잠금 장치 3 개
PostgreSQL 자문 잠금: Postgres 클러스터 내에서 잠금 (프로세스/세션).
모든 중요한 섹션이 이미 동일한 데이터베이스에있을 때 좋습니다.
sql
SELECT pg_try_advisory_lock(42); -- take
SELECT pg_advisory_unlock(42); -- let go
4. 파일/클라우드 잠금 장치 4
'If-Match' (ETag) 조건이있는 S3/GCS + 객체 메타 데이터 잠금 → 기본적으로 CAS.
백업/마이그레이션에 적합합니다.
5) 안전 잠금 설계
5. 1 소유자 정체성
'owner _ id' (# 프로세스 # pid # star _ time node) + 잠금 해제 확인을위한 임의의 토큰.
다른 사람의 자물쇠를 제거해서는 안됩니다.
5. TTL 및 확장 2 개
(PHP 3 = 3.0.6, PHP 4)
갱신-마감일과 함께 정기적으로 (예: 모든 'TTL/3').
5. 3 타박상에 펜싱 토큰
외부 자원을 수정하는 섹션은 'fencing _ token' 을 통과해야합니다.
싱크 (DB/캐시/스토리지) 는 '마지막 _ 토큰' 을 저장하고 작은 것을 거부합니다
sql
UPDATE wallet
SET balance = balance +:delta, last_token =:token
WHERE id =:id AND:token > last_token;
5. 4 대기 대기열과 정의
ZK- 'EPHEMERAL _ SEQUENTIAL' 및 관찰자: 클라이언트가 가장 가까운 이전 모델의 출시를 기다리고 있습니다.
etcd에서-개정/버전 지정이있는 키; (PHP 3 = 3.0.6, PHP 4)
5. 5 분할 뇌 행동
CP 접근 방식: 쿼럼이 없으면 잠금 장치를 사용할 수 없습니다. 안전을 위반하는 것보다 서 있는 것이 좋습니다.
AP 접근 방식: 분할 된 섬에서 진행이 허용됩니다 → 펜싱이 필요합니다.
6) 지도자 선거
etcd/ZK에서 "리더" 는 독점적 인 에페 메릭 키입니다. 나머지는 변경에 가입합니다.
지도자는 심장 박동을 씁니다. 손실-재선.
펜싱 토큰 (시대/개정 번호) 으로 모든 리더 운영을 동반합니다.
7) 오류 및 처리
클라이언트는 자물쇠를 가져 갔지만 작동하기 위해 충돌합니다 → 표준은 아무도 고통받지 않을 것입니다. TTL/세션이 릴리스됩니다.
작업 중에 잠금이 만료되었습니다
필수 워치 독: 확장이 실패하면 중요한 섹션을 중단하고 롤백/보상하십시오.
"나중에 마무리" 없음: 잠금 장치가 없으면 중요한 섹션을 계속할 수 없습니다.
긴 일시 정지 (GC/stop-the-world) → 확장이 발생하지 않았고 다른 하나는 잠금 장치를 사용했습니다. 워크 플로우는 소유권 손실 (keepalive 채널) 및 중단을 감지해야합니다.
8) Dedloki, 우선 순위 및 반전
분산 된 세계의 Dedloki는 드물지만 (일반적으로 하나의 성이 있음) 여러 개의 성이있는 경우 단일 주문 (잠금 주문) 을 준수하십시오.
우선 순위 반전: 우선 순위가 낮은 소유자는 리소스를 보유하고 우선 순위가 높은 소유자는 대기합니다. 솔루션: TTL 제한, 선점 (비즈니스가 허용하는 경우), 리소스 선명도.
금식: 공정성을 위해 대기 대기 대기열 (ZK 하위 순서 노드) 을 사용하십시오.
9) 관찰 가능성
메트릭:- 'lock _ recove _ total {상태 = ok' Timeout 'orr}'
- 'lock _ hold _ secons {p50, p95, p99}'
- (PHP 3 = 3.0.6, PHP 4)
- (PHP 3 = 3.0.6, PHP 4)
- (PHP 3 = 3.0.6, PHP 4)
- 'preemptions _ total', 'wait _ queu _ len'
- 'lock _ 팔로워', 'owner _ id', 'token', 'ttl', '시도', 'wait _ time _ ms', 'path' (계정 ZK), 'mod _ 개정' (etcd).
- "임계 → 중요 섹션 → 릴리스" 는 결과와 함께 제공됩니다.
- 성장 'lease _ renew _ fail _ total'.
- 'lock _ hold _ secons {p99}'> SLO.
- "고아" 잠금 (심장 박동없이).
- 부풀어 오른 대기자 명단.
10) 사례 연구
10. 펜싱이있는 보안 리디스 잠금 1 개 (의사)
1. 토큰 카운터를 신뢰할 수있는 상점 (예: Postgres/etcd) 에 저장합니다.
2. 'SET NX PX' 가 성공하면 토큰을 읽거나 증가시키고 데이터베이스/서비스에서 토큰을 확인하여 리소스를 모두 변경합니다.
python acquire token = db. next_token ("locks/orders/42") # monotone ok = redis. set("locks:orders:42", owner, nx=True, px=ttl_ms)
if not ok:
raise Busy()
critical op guarded by token db. exec("UPDATE orders SET... WHERE id=:id AND:token > last_token",...)
release (compare owner)
10. 2 etcd Mutex + 워치 독 (Go)
go ctx, cancel:= context. WithCancel(context. Background())
sess, _:= concurrency. NewSession(cli, concurrency. WithTTL(10))
m:= concurrency. NewMutex(sess, "/locks/job/cleanup")
if err:= m. Lock(ctx); err!= nil { /... / }
// Watchdog go func() {
<-sess. Done ()//loss of session/quorum cancel ()//stop working
}()
doCritical (ctx )//must respond to ctx. Done()
_ = m. Unlock(context. Background())
_ = sess. Close()
10. ZK의 3 리더십 (자바, 큐레이터)
java
LeaderSelector selector = new LeaderSelector(client, "/leaders/cron", listener);
selector. autoRequeue();
selector. start(); // listener. enterLeadership() с try-finally и heartbeat
10. 마감일이있는 4 개의 Postgres 자문 잠금 (SQL + 앱)
sql
SELECT pg_try_advisory_lock(128765); -- attempt without blocking
-- if false --> return via backoff + jitter
11) 테스트 플레이 북 (게임 데이)
정원 손실: 1-2 etcd 노드 비활성화 → 잠금 시도는 통과해서는 안됩니다.
GC 일시 정지/세계 정지: 소유자의 흐름을 인위적으로 지연시킵니다 → 워치 독이 작업을 중단하고 있는지 확인하십시오.
분할 뇌: 소유자와 성 측면 사이의 네트워크 분리 에뮬레이션 → 새로운 소유자는 더 높은 펜싱 토큰을 얻습니다. 오래된 소유자는 파란색으로 거부됩니다.
시계 왜곡/드리프트: 소유자로부터 시계를 가져 가십시오 (Redis/lies의 경우) → 토큰/체크로 정확성을 보장합니다.
출시 전 충돌: 프로세스 충돌 → 잠금은 TTL/세션을 통해 해제됩니다.
12) 반 패턴
외부 리소스에 액세스 할 때 펜싱없이 TTL 잠금을 청소하십시오.
정확성을 위해 현지 시간에 의존합니다 (HLC/펜싱 없음).
feilover가 있고 복제본을 확인하지 않은 환경에서 하나의 Redis 마스터를 통한 자물쇠 배포.
무한 중요 섹션 (TTL "연령").
'owner _ id '/토큰을 확인하지 않고 다른 사람의 잠금 장치를 제거합니다.
백오프 부족 + 지터 → 시도의 폭풍.
"모든 것을위한" 단일 글로벌 잠금 장치-갈등의 가방; 키 샤딩이 더 좋습니다.
13) 구현 점검표
- 자원 유형이 정의되어 있으며 CAS/큐/idempotency를 분배 할 수 있습니다.
- 메커니즘 선택: CP를위한 etcd/ZK/Consul; 리디스/캐시-펜싱에만 해당됩니다.
- 구현: 'owner _ id', TTL + 확장, 워치 독, 올바른 잠금 해제.
- 외부 자원은 펜싱 토큰 (단조) 을 확인합니다.
- 리더십 전략과 실패가 있습니다.
- 구체화 된 메트릭, 경고, 로깅 토큰 및 개정.
- Backoff + jitter 및 획득 타임 아웃이 제공됩니다.
- 게임 일: 쿼럼, 분할 뇌, GC 일시 정지, 시계 왜곡.
- 여러 잠금 장치 복용 절차 문서 (필요한 경우).
- 브라운 아웃 계획-잠금 장치를 사용할 수 없을 때해야 할 일.
14) FAQ
Q: 'SET NX PX' Redis가 충분히 잠겨 있습니까?
A: 자원이 펜싱 토큰을 확인하는 경우에만 가능합니다. 그렇지 않으면 네트워크 분할을 사용하면 두 명의 소유자가 가능
Q: "기본적으로" 무엇을 선택해야합니까?
A: 엄격한 보증을 위해-etcd/ZooKeeper/Consul (CP). 하나의 데이터베이스 내부에서 쉬운 작업을 위해-자문 잠금 Postgres Redis-펜싱으로 만.
Q: 어떤 TTL을 넣을까요?
A: 'TTL 소 p99 임계 섹션 지속 시간 × 2' 및 "좀비를 신속하게 제거 할 수있을 정도로 짧습니다. "갱신-모든 'TTL/3'.
Q: 금식을 피하는 방법?
A: 대기 대기열 (ZK 순차) 또는 공정성 알고리즘; 시도의 한계와 공정한 계획.
Q: 시간 동기화가 필요합니까?
A: 정확성을 위해-아니오 (펜싱 사용). 작동 예측 가능성의 경우 예 (NTP/PTP) 이지만 잠금 로직을 위해 벽 시계에 의존하지 마십시오.
15) 총계
신뢰할 수있는 분산 잠금 장치는 임대 + 기념품이있는 쿼럼 레벨 (etcd/ZK/Consul) 에 구축되며 반드시 변경되는 리소스 레벨에서 펜싱 토큰으로 보완됩니다. 펜싱없이 TTL/Redis 접근 방식은 분할 뇌 위험입니다. 인과 관계와 민첩성에 대해 먼저 생각하고, 불가능한 곳에서 잠금 장치를 사용하고, 실패 모드를 측정하고, 테스트하고, "중요한 섹션" 은 사건 수가 아닌 의미에서만 중요합니다.