Redis Nx를 사용하여 분산 잠금을 구현하는 방법은 무엇입니까?

로컬 잠금은 자신이 위치한 가상 머신의 스레드 동기 실행만 제어할 수 있습니다.이제 분산 환경의 모든 가상 머신에서 스레드의 동기 실행을 달성하려면 여러 가상 머신이 잠금을 공유해야 합니다.가상 머신을 배포할 수 있습니다. 분산 방식으로, 아래와 같이 분산 방식으로 배포할 수도 있습니다.

가상 머신은 모두 동일한 잠금을 보유하고 있으며, 잠금은 잠금 및 잠금 해제 서비스를 제공하는 별도의 프로그램이며, 잠금을 획득한 사람은 누구나 데이터베이스에 쿼리합니다.

잠금은 더 이상 특정 가상 머신에 속하지 않고 분산 방식으로 배포되어 여러 가상 머신에서 공유됩니다.이 잠금을 분산 잠금이라고 합니다.

분산 잠금 구현 솔루션

분산 잠금을 구현하는 솔루션에는 여러 가지가 있으며 일반적으로 사용되는 솔루션은 다음과 같습니다.

1. 데이터베이스 기반 분산 잠금 구현

데이터베이스의 기본 키 또는 데이터베이스의 고유 인덱스의 고유성을 활용하면 여러 스레드가 동시에 동일한 레코드를 삽입할 수 있으며, 삽입에 성공한 사람이 잠금을 잡게 됩니다.

2. Redis 기반 잠금 구현

Redis는 SETNX, set nx, redisson 등과 같은 분산 잠금 구현 솔루션을 제공합니다.

SETNX를 예로 들면, SETNX 명령의 작업 프로세스는 존재하지 않는 키를 설정하는 것입니다.여러 스레드가 동일한 키를 설정하면 단 하나의 스레드만 이를 성공적으로 설정하고 설정이 성공한 스레드가 잠금을 얻습니다.

3. 사육사를 사용하여 구현

Zookeeper는 주로 분산 프로그램 간의 동기화 문제를 해결하는 분산 조정 서비스입니다. Zookeeper의 구조는 파일 디렉터리와 유사하며 여러 스레드가 Zookeeper에 하위 디렉터리(노드)를 생성하면 하나만 생성됩니다. 이 기능을 사용하면 분산 잠금을 구현할 수 있습니다. 노드를 성공적으로 생성한 사람이 잠금을 얻습니다.

Redis NX는 분산 잠금을 구현합니다.

Redis에서 분산 잠금을 구현하기 위한 솔루션은 redis.cn 웹사이트(http://www.redis.cn/commands/set.html)에서 찾을 수 있습니다.

이를 달성하려면 다음 명령을 사용하십시오. SET Resource-name anystring NX EX max-lock-time.

NX: 설정이 성공하기 전에는 키가 존재하지 않음을 나타냅니다.

예: 만료 시간 설정

여기에서 세 개의 SSH 클라이언트를 시작하고 redis에 연결합니다: docker exec -it redis redis-cli

첫 번째 인증: auth redis

다음과 같이 동시에 3개의 클라이언트에 테스트 명령을 보냅니다.

lock001 잠금 설정을 나타내며 값은 001, 만료 시간은 30초입니다.

Plain Text
SET lock001 001 NX EX 30

명령이 성공적으로 전송되었으며 3개의 SSH 클라이언트를 관찰한 결과 한 개만 성공적으로 설정되고 나머지 두 개는 설정이 실패한 것으로 나타났습니다. 성공적인 설정 요청은 lock001 잠금이 획득되었음을 의미합니다.

분산 잠금을 구현하기 위해 코드에서 Set nx를 사용하는 방법은 무엇입니까?

set nx는 spring-boot-starter-data-redis에서 제공하는 API를 사용하여 구현할 수 있습니다. 종속성을 추가합니다.

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
    <groupId>org.apache.commons</groupId>
    <artifactId>commons-pool2</artifactId>
    <version>2.6.2</version>
</dependency>

종속성을 추가한 후 빈에 RestTemplate을 삽입하십시오. 먼저 다음과 같이 의사 코드 조각을 분석해 보겠습니다.

if(缓存中有){

  返回缓存中的数据
}else{

  获取分布式锁
  if(获取锁成功){
       try{
         查询数据库
      }finally{
         释放锁
      }
  }
 
}

1. 분산 잠금 획득

잠금을 얻으려면 redisTemplate.opsForValue().setIfAbsent(key,vaue)를 사용하세요.

여기서 질문을 생각해 보세요. 키/값을 nx로 설정하면 키(즉, 잠금)가 만료 시간을 설정해야 합니까?

만료 시간을 설정하지 않고 잠금을 획득하고 최종적으로 실행하지 않으면 잠금은 항상 존재하며 다른 스레드는 잠금을 획득할 수 없습니다. 따라서 set nx를 실행할 때 만료 시간을 지정해야 하는데, 즉 다음 명령어를 사용한다.

SET resource-name anystring NX EX max-lock-time

구체적인 호출 방법은 다음과 같습니다: redisTemplate.opsForValue().setIfAbsent(K var1, V var2, long var3, TimeUnit var5)

2. 잠금 해제 방법

잠금을 해제하는 상황에는 키 만료 시 자동 해제와 수동 삭제라는 두 가지 상황이 있습니다.

1) 키 만료 시 자동으로 키를 해제하는 방법

Lock에는 만료 시간이 설정되어 있으므로 만료되면 키가 자동으로 해제되는데, 데이터베이스 쿼리 등의 작업이 완료되기 전에 키가 만료되는 문제가 있는데, 이때 다른 스레드가 잠금을 잡고, 마지막으로 데이터베이스를 반복적으로 쿼리합니다. 반복되는 비즈니스 작업입니다.

이 문제를 해결하는 방법?

키 만료 시간은 데이터베이스 쿼리, 캐시 설정 및 기타 관련 작업을 완료할 수 있을 만큼 길게 설정할 수 있습니다.

그렇게 되면 효율이 낮아지고, 시간가치도 조절하기 어려워진다.

2) 잠금을 수동으로 삭제

수동으로 잠금을 삭제하는 경우, 만료 시 키 자동 삭제와 충돌하여 다른 사람의 잠금이 삭제될 수 있습니다.

예를 들어, 데이터베이스 쿼리 및 기타 비즈니스 작업이 완료되지 않은 경우 키가 만료되고 이때 다른 스레드가 잠금을 점유합니다. 이전 스레드가 데이터베이스 쿼리 및 기타 비즈니스 작업을 실행할 때 수동으로 삭제합니다. lock은 다른 스레드의 잠금을 삭제합니다.

이 문제를 해결하려면 잠금을 삭제하기 전에 잠금이 스스로 설정되었는지 확인하는 방법을 사용할 수 있습니다.의사 코드는 다음과 같습니다.

if(缓存中有){

  返回缓存中的数据
}else{

  获取分布式锁: set lock 01 NX
  if(获取锁成功){
       try{
         查询数据库
      }finally{
         if(redis.call("get","lock")=="01"){
            释放锁: redis.call("del","lock")
         }
         
      }
  }
 
}

위 코드의 11~13행은 비원자적이며 다른 스레드의 잠금도 삭제되도록 합니다. 문서의 지침을 확인하세요: http://www.redis.cn/commands/set.html

위의 최적화 방법은 클라이언트 a가 획득한 잠금(키)이 만료 시간으로 인해 Redis 서버에 의해 삭제되었지만 클라이언트 A는 이 시점에도 여전히 DEL 명령을 실행하는 시나리오를 방지합니다. 클라이언트 A가 만료 시간을 설정한 후 클라이언트 b는 동일한 키의 잠금을 다시 획득했으므로 클라이언트 A에서 DEL을 실행하면 클라이언트 b가 추가한 잠금이 해제됩니다.

잠금 해제 스크립트의 예는 다음과 같습니다.

if redis.cal1("get",KEYS[1]) == ARGV[1]
then
    return redis. call("del",KEYS[1])
else
    return 0
end

키/값을 설정하기 위해 setnx 명령을 호출할 때 스레드마다 서로 다른 값을 설정하므로 스레드가 잠금을 삭제할 때 먼저 키를 기반으로 당시에 설정한 값인지 확인하기 위해 쿼리할 수 있으며, 그렇다면 삭제하세요.

이 전체 작업은 원자적이며 구현 방법은 위의 Lua 스크립트를 실행하는 것입니다.

Lua는 컴팩트 스크립팅 언어입니다. 버전 2.6에서 redis는 여러 명령의 원자성을 보장하기 위해 Lua 스크립트 실행을 지원합니다.

원자성이란 무엇입니까?

이러한 지침은 모두 성공하거나 모두 실패합니다.

위는 Redis Nx를 사용하여 분산 잠금을 구현한 것으로, 다른 스레드에서 설정한 잠금을 삭제하지 않으려면 Redis를 사용하여 Lua 스크립트를 실행해야 하는데 이는 원자적이지만 만료 시간 값 설정에는 부정확성이 없습니다. .

추천

출처blog.csdn.net/zy1992As/article/details/132691398