분산 잠금의 원리와 Redis가 분산 잠금을 구현하는 방법에 대해 이야기해 보겠습니다.

藏色散人
풀어 주다: 2023-01-27 07:30:01
앞으로
1162명이 탐색했습니다.

이 기사는 분산 잠금이 무엇인지 주로 소개하는 Redis에 대한 관련 지식을 제공합니다. Redis는 분산 잠금을 어떻게 구현합니까? 어떤 조건을 충족해야 합니까? 아래 내용을 살펴보겠습니다. 도움이 필요한 친구들에게 도움이 되기를 바랍니다.

1. 분산 잠금의 기본 원칙

분산 잠금: 분산 시스템 또는 클러스터 모드에서 여러 프로세스에 표시되고 상호 배타적인 잠금입니다.

분산 잠금이 충족해야 하는 조건:

  • 가시성: 여러 스레드가 동일한 결과를 볼 수 있습니다. 참고: 여기서 언급하는 가시성은 동시 프로그래밍의 메모리 가시성이 아니라 단지 여러 프로세스가 변경 사항을 인식할 수 있다는 의미입니다.
  • 상호 배제: 상호 배제는 분산의 가장 기본적인 조건입니다.
  • 고가용성: 프로그램이 쉽게 충돌하지 않아 항상 고가용성을 보장합니다.
  • 고성능: 잠금 자체로 인해 성능이 저하됩니다. 모든 분산 잠금 자체에는 더 높은 잠금 성능이 필요합니다. 및 잠금 해제 성능
  • 안전성: 보안도 프로그램의 필수 부분입니다

세 가지 일반적인 분산 잠금이 있습니다:

  • Mysql: mysql 자체에는 잠금 메커니즘이 있지만 mysql의 평균 성능으로 인해 , 분산 잠금을 사용할 때 실제로 mysql을 분산 잠금으로 사용하는 경우는 상대적으로 드뭅니다

  • Redis: 분산 잠금으로서의 redis는 요즘에는 기본적으로 redis나 Zookeeper를 사용하는 방식이 매우 일반적입니다. setnx 메소드를 사용하여 키가 성공적으로 삽입되면 누군가가 성공적으로 삽입하면 다른 사람은 잠금을 얻을 수 없음을 나타냅니다. 분산 잠금 구현

  • Zookeeper: Zookeeper는 엔터프라이즈 수준 개발에서 분산 잠금을 구현하는 데에도 더 나은 솔루션입니다

분산 잠금의 원리와 Redis가 분산 잠금을 구현하는 방법에 대해 이야기해 보겠습니다.

2. Redis Type 잠금을 기반으로 배포를 구현합니다.

두 가지 기본 방법에 필요한 분산 잠금 구현 시 구현:

  • 잠금 획득:

    • 상호 배제: 단 하나의 스레드만 잠금을 획득할 수 있도록 보장
    • 비차단: 한 번 시도하고 성공적으로 true를 반환하고, 실패하면 false를 반환
  • 잠금 해제:

    • 수동 해제
    • 시간 초과 해제: 잠금 획득 시 시간 초과 추가

Redis 기반 분산 잠금 원칙 구현:

SET resource_name my_random_value NX PX 30000
로그인 후 복사
  • resource_name: 리소스 이름, 다른 잠금 가능 업종에 따라 구별
  • my_random_value : 랜덤값, 각 스레드의 랜덤값이 다르며, 잠금해제 시 검증에 사용
  • NX : 키가 없을 때 성공적으로 설정, 키가 있을 경우 설정 실패
  • PX: 자동 만료 시간, 예외가 발생하면 잠금이 만료되고 만료될 수 있습니다.

NX의 원자성을 사용하면 여러 스레드가 동시에 실행될 때 하나의 스레드만 성공적으로 설정될 수 있습니다. 성공적인 설정은 잠금을 획득했음을 의미합니다. 후속 비즈니스 처리가 가능합니다. 예외가 발생하고 잠금이 만료되면 잠금이 자동으로 해제됩니다.

1. Redis를 기반으로 분산 잠금을 구현합니다. - RedisLock

public interface ILock extends AutoCloseable {
    /**
     * 尝试获取锁
     *
     * @param timeoutSec 锁持有的超时时间,过期后自动释放
     * @return true代表获取锁成功;false代表获取锁失败
     */
    boolean tryLock(long timeoutSec);

    /**
     * 释放锁
     * @return
     */
    void unLock();
}
로그인 후 복사
잠금 실수 삭제 문제

문제 설명:

잠금을 보유하고 있는 스레드 1이 잠금 내부에서 차단되었습니다. 이때 스레드 2는 시간 초과 후 자동으로 잠금을 해제하려고 합니다. 잠금을 획득한 후 스레드 2가 잠금을 유지하면서 실행됩니다. 이 과정에서 스레드 1이 반응하여 실행을 계속하고 이때 스레드 2에 속해야 하는 잠금이 삭제됩니다. 실수로 자물쇠를 삭제한 경우.

해결책:

잠금을 저장할 때 자신의 스레드 ID를 입력하세요. 잠금을 삭제할 때 현재 잠금의 ID가 저장한 ID인지 판단하세요. 그렇지 않다면 삭제하세요. , 삭제가 수행되지 않습니다.

버전 2: 실수로 잠금을 삭제하는 문제 해결

public class SimpleRedisLock {
    private final StringRedisTemplate stringRedisTemplate;
    private final String name;

    public SimpleRedisLock(StringRedisTemplate stringRedisTemplate, String name) {
        this.stringRedisTemplate = stringRedisTemplate;
        this.name = name;
    }

    private static final String KEY_PREFIX = "lock:";

    @Override
    public boolean tryLock(long timeoutSec) {
        //获取线程标识
        String threadId = Thread.currentThread().getId();
        //获取锁
        Boolean success = stringRedisTemplate.opsForValue()
                .setIfAbsent(KEY_PREFIX + name, threadId, timeoutSec, TimeUnit.SECONDS);
        return Boolean.TRUE.equals(success);
    }

    @Override
    public void unLock() {
        //通过del删除锁
        stringRedisTemplate.delete(KEY_PREFIX + name);
    }

    @Override
    public void close() {
        unLock();
    }
}
로그인 후 복사
잠금 해제의 원자성 문제

문제 분석:

위의 잠금을 해제하는 코드에는 여전히 스레드 1이 실수로 잠금을 삭제하는 문제가 있습니다. 이때, 잠금이 만료되면 자동으로 잠금이 해제되는 현상이 발생합니다. 이때 스레드 1은 여전히 ​​잠금 해제 작업을 수행하므로 스레드 2가 보유한 잠금이 실수로 삭제됩니다.

이유는 Java 코드로 구현된 잠금 해제 프로세스가 원자적 연산이 아니며 스레드 안전성 문제가 있기 때문입니다.

해결책:

Redis는 Lua 스크립트 기능을 제공합니다. 하나의 스크립트에 여러 Redis 명령을 작성하면 여러 명령 실행의 원자성을 보장할 수 있습니다.

버전 3: Lua 스크립트를 호출하여 분산 잠금 변환

public class SimpleRedisLock {
    private final StringRedisTemplate stringRedisTemplate;
    private final String name;

    public SimpleRedisLock(StringRedisTemplate stringRedisTemplate, String name) {
        this.stringRedisTemplate = stringRedisTemplate;
        this.name = name;
    }

    private static final String KEY_PREFIX = "lock:";
    private static final String ID_PREFIX = UUID.randomUUID().toString(true) + "-";

    @Override
    public boolean tryLock(long timeoutSec) {
        //获取线程标识
        String threadId = ID_PREFIX + Thread.currentThread().getId();
        //获取锁
        Boolean success = stringRedisTemplate.opsForValue()
                .setIfAbsent(KEY_PREFIX + name, threadId, timeoutSec, TimeUnit.SECONDS);
        return Boolean.TRUE.equals(success);
    }

    @Override
    public void unLock() {
        // 获取线程标示
        String threadId = ID_PREFIX + Thread.currentThread().getId();
        // 获取锁中的标示
        String id = stringRedisTemplate.opsForValue().get(KEY_PREFIX + name);
        // 判断标示是否一致
        if(threadId.equals(id)) {
            // 释放锁
            stringRedisTemplate.delete(KEY_PREFIX + name);
        }
    }

    @Override
    public void close() {
        unLock();
    }
}
로그인 후 복사
권장 학습: "

Redis 비디오 튜토리얼

"

위 내용은 분산 잠금의 원리와 Redis가 분산 잠금을 구현하는 방법에 대해 이야기해 보겠습니다.의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

관련 라벨:
원천:juejin.im
본 웹사이트의 성명
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.
최신 이슈
인기 튜토리얼
더>
최신 다운로드
더>
웹 효과
웹사이트 소스 코드
웹사이트 자료
프론트엔드 템플릿