다음 코드의 문제를 분석하세요:
// 分布式锁服务 public interface RedisLockService { // 获取锁 public boolean getLock(String key); // 释放锁 public boolean releaseLock(String key); } // 业务服务 public class BizService { @Resource private RedisLockService redisLockService; public void bizMethod(String bizId) { try { // 获取锁 if(redisLockService.getLock(bizId)) { // 业务重复校验 if(!bizValidate(bizId)) { throw new BizException(ErrorBizCode.REPEATED); } // 执行业务 return doBusiness(); } // 获取锁失败 throw new BizException(ErrorBizCode.GET_LOCK_ERROR); } finally { // 释放锁 redisLockService.releaseLock(bizId); } } }
위 코드는 괜찮아 보이지만 실제로는 큰 문제가 숨겨져 있습니다. 문제는 잠금을 해제할 때 현재 스레드가 잠금을 획득했는지 여부를 확인할 수 없다는 것입니다.
스레드 1과 스레드 2가 동시에 비즈니스 메서드에 액세스합니다.
스레드 2가 성공적으로 잠금을 획득하고 비즈니스 처리를 수행합니다
스레드 1이 획득하지 못했습니다. 잠금을 획득했지만 잠금이 해제되었습니다.
이때 스레드 3은 성공적으로 잠금 획득을 시도했지만 스레드 2의 비즈니스는 완료되지 않았습니다. 처리되므로 스레드 3은 비즈니스 중복 예외가 발생하지 않습니다
결국 스레드 2와 스레드 3이 반복됩니다. 비즈니스 실행
해결책은 확인 후에만 잠금을 해제하도록 허용하는 것입니다. 잠금 획득이 성공했다는 것:
public class BizService { @Resource private RedisLockService redisLockService; public void bizMethod(String bizId) { boolean getLockSuccess = false; try { // 尝试获取锁 getLockSuccess = redisLockService.getLock(bizId); // 获取锁成功 if(getLockSuccess) { // 业务重复校验 if(!bizValidate(bizId)) { throw new BizException(ErrorBizCode.REPEATED); } // 执行业务 return doBusiness(); } // 获取锁失败 throw new BizException(ErrorBizCode.GET_LOCK_ERROR); } finally { // 获取锁成功才允许释放锁 if(getLockSuccess) { redisLockService.releaseLock(bizId); } } } }
두 번째 문제는 Redis에도 메모리 정리 메커니즘이 있어서 분산 잠금이 실패할 수 있다는 것입니다.
(1) 일반 삭제
Redis는 어떤 키가 만료되었는지 정기적으로 확인하고 만료된 것으로 확인되면 삭제합니다.
(2) 지연 삭제
키가 너무 많은 경우 키가 많으면 일반 삭제는 매우 비싼 리소스이므로 지연 삭제 전략이 도입됩니다
Redis가 키에 액세스할 때 키가 만료된 것을 발견하면 직접 삭제됩니다
메모리가 부족한 경우 , Redis는 삭제할 일부 요소를 선택합니다:
no-enviction
데이터 제거 금지, 새로운 쓰기 작업은 오류를 보고합니다
휘발성-lru
만료 시간이 다음으로 설정된 데이터 세트에서 가장 최근에 사용되지 않은 데이터를 선택합니다. 제거
휘발성-ttl
만료 시간이 설정된 데이터 세트에서 만료할 데이터를 선택하여 제거합니다.
휘발성-random
제거할 데이터 세트에서 임의의 데이터를 선택합니다.
allkeys-lru
다음을 선택합니다. 제거를 위해 데이터 세트에서 가장 최근에 사용된 데이터
allkeys-random
데이터 세트에서 임의 선택 데이터 제거
분산 잠금 실패로 이어지는 시나리오는 최소한 두 가지가 있습니다.
시나리오 1: Redis에는 다음을 위한 메모리가 부족합니다. allkeys-lru
或者allkeys-random
재활용 전략을 사용하면 잠금 실패
시나리오 2: 스레드가 분산 잠금을 성공적으로 획득했지만 비즈니스 처리 시간이 너무 길어집니다. 이때 잠금이 만료되고 정기적으로 지워져 다른 스레드가 발생합니다. 성공적으로 잠금을 획득하고 비즈니스를 반복적으로 실행하려면
일반적인 해결책은 데이터베이스 계층에서 이를 보호하는 것입니다. 예를 들어 재고 차감 사업은 데이터베이스 계층에서 낙관적 잠금을 사용합니다.
아아아아위 내용은 Redis 분산 잠금이 피해야 하는 두 가지 함정은 무엇입니까?의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!