PHP 는 Redis 증빙 자 물 쇠 를 실현 하고 중복 기록 을 방지 합 니 다.

1.앞 에 쓰 기:
전체 공급 체인 시스템 에서 여러 가지 증빙 서류(구 매 서,입고 서,화물 송장,운송장 등)가 있 을 것 이다.증빙 서 류 를 작성 하 는 데이터 와 관련 된 인터페이스(첨삭 조작)는 전단 에 관련 제한 을 하 더 라 도 네트워크 나 이상 조작 으로 인해 중복 호출 되 는 상황 이 발생 하여 같은 증빙 서 류 를 똑 같이 처리 할 수 있다.
이러한 상황 이 시스템 에 이상 한 영향 을 미 치 는 것 을 방지 하기 위해 우 리 는 Redis 를 통 해 간단 한 증빙 자 물 쇠 를 실현 했다.모든 요청 은 먼저 자 물 쇠 를 가 져 와 야 업무 논 리 를 수행 할 수 있 고 실행 이 끝 난 후에 야 자 물 쇠 를 풀 수 있다.같은 증빙서류 의 동시 반복 작업 요청 이 하나의 요청 만 잠 금(Redis 에 의존 하 는 단일 스 레 드)을 얻 을 수 있 도록 하 는 비관 적 인 잠 금 디자인 입 니 다.
주:Redis 는 우리 시스템 에 잠 겨 있 습 니 다.보통 중복 요청 을 해결 하 는 데 만 사 용 됩 니 다.동시 다발 적 이지 않 은 중복 요청 에 대해 데이터 베이스 나 로그 로 데 이 터 를 검사 하 는 상 태 를 확인 합 니 다.두 가지 체 제 를 결합 해 야 전체 링크 의 신뢰성 을 확보 할 수 있 습 니 다.
2.잠 금 메커니즘:
주로 Redis setnx 명령 에 의존 하여 이 루어 집 니 다.

그러나 setnx 를 사용 하 는 데 문제 가 있 습 니 다.즉,setnx 명령 은 만 료 시간 을 설정 하 는 것 을 지원 하지 않 습 니 다.expire 명령 을 사용 해 야 합 니 다.다른 행위 key 설정 시간 초과 시간 을 설정 해 야 합 니 다.그러면 전체 잠 금 작업 은 원자 작업 이 아니 라 setnx 잠 금 추가 성공 이 있 을 수 있 습 니 다.그러나 프로그램 이 이상 하 게 종료 되 어 시간 초과 시간 을 설정 하지 못 했 습 니 다.제때에 잠 금 을 풀 지 않 은 상황 에서잠 금 이 걸 릴 수 있 습 니 다.
이러한 상황 은 Redis 사 무 를 사용 하여 해결 할 수 있 습 니 다.setnx 와 expire 두 가지 명령 을 원자 적 인 조작 으로 실행 할 수 있 습 니 다.그러나 이렇게 하면 상대 적 으로 번 거 로 울 수 있 습 니 다.예 를 들 어 Redis 2.6.12 이후 버 전에 서 Redis set 명령 은 nx,ex 모드 를 지원 하고 원자 화 된 기한 이 지난 시간 을 설정 할 수 있 습 니 다.

3.잠 금 실현(전체 테스트 코드 는 마지막 에 붙 입 니 다):

 /**
  *     
  * @param int $intOrderId   ID
  * @param int $intExpireTime      ( )
  * @return bool|int          ID,      false
  */
 public static function addLock($intOrderId, $intExpireTime = self::REDIS_LOCK_DEFAULT_EXPIRE_TIME)
 {
  //    
  if (empty($intOrderId) || $intExpireTime <= 0) {
   return false;
  }

  //  Redis  
  $objRedisConn = self::getRedisConn();

  //     ID,      ID
  $intUniqueLockId = self::generateUniqueLockId();

  //    ,    ID,    Redis key(    ,  ID          )
  $strKey = sprintf(self::REDIS_LOCK_KEY_TEMPLATE, $intOrderId);

  //  (  Redis setnx    , Redis 2.6.12  ,  set           setnx,             )
  $bolRes = $objRedisConn->set($strKey, $intUniqueLockId, ['nx', 'ex'=>$intExpireTime]);

  //       ID,      false
  return $bolRes ? $intUniqueLockId : $bolRes;
 }
4.잠 금 해제 메커니즘:
잠 금 해제 란 잠 금 을 추가 할 때 유일한 lock id 보다 성공 하면 key 를 삭제 합 니 다.주의해 야 할 것 은 잠 금 해제 전체 과정 에서 원자 성 을 확보 해 야 한 다 는 것 이다.여 기 는 redis 의 watch 와 업무 실현 에 의존 해 야 한다.
WATCH 명령 은 한 개 이상 의 키 를 감시 할 수 있 으 며,한 개의 키 가 수정 되 거나 삭제 되면 이후 의 업 무 는 실행 되 지 않 습 니 다.EXEC 명령 까지 모니터링(트 랜 잭 션 의 명령 은 EXEC 이후 에 실행 되 기 때문에 MULTI 명령 후 WATCH 모니터링 키 를 수정 할 수 있 습 니 다)
5.잠 금 해제 실현(전체 테스트 코드 는 마지막 에 붙 여 집 니 다):

/**
  *     
  * @param int $intOrderId   ID
  * @param int $intLockId    ID
  * @return bool
  */
 public static function releaseLock($intOrderId, $intLockId)
 {
  //    
  if (empty($intOrderId) || empty($intLockId)) {
   return false;
  }

  //  Redis  
  $objRedisConn = self::getRedisConn();

  //  Redis key
  $strKey = sprintf(self::REDIS_LOCK_KEY_TEMPLATE, $intOrderId);

  //  Redis key   【  lock id】 【         】      ,            ,           
  $objRedisConn->watch($strKey);
  if ($intLockId == $objRedisConn->get($strKey)) {
   $objRedisConn->multi()->del($strKey)->exec();
   return true;
  }
  $objRedisConn->unwatch();
  return false;
 }
6.전체 테스트 코드 를 첨부 합 니 다.

<?php

/**
 * Class Lock_Service      
 */
class Lock_Service
{
 /**
  *    redis key  
  */
 const REDIS_LOCK_KEY_TEMPLATE = 'order_lock_%s';

 /**
  *          ( )
  */
 const REDIS_LOCK_DEFAULT_EXPIRE_TIME = 86400;

 /**
  *     
  * @param int $intOrderId   ID
  * @param int $intExpireTime      ( )
  * @return bool|int          ID,      false
  */
 public static function addLock($intOrderId, $intExpireTime = self::REDIS_LOCK_DEFAULT_EXPIRE_TIME)
 {
  //    
  if (empty($intOrderId) || $intExpireTime <= 0) {
   return false;
  }

  //  Redis  
  $objRedisConn = self::getRedisConn();

  //     ID,      ID
  $intUniqueLockId = self::generateUniqueLockId();

  //    ,    ID,    Redis key(    ,  ID          )
  $strKey = sprintf(self::REDIS_LOCK_KEY_TEMPLATE, $intOrderId);

  //  (  Redis setnx    , Redis 2.6.12  ,  set           setnx,             )
  $bolRes = $objRedisConn->set($strKey, $intUniqueLockId, ['nx', 'ex'=>$intExpireTime]);

  //       ID,      false
  return $bolRes ? $intUniqueLockId : $bolRes;
 }

 /**
  *     
  * @param int $intOrderId   ID
  * @param int $intLockId    ID
  * @return bool
  */
 public static function releaseLock($intOrderId, $intLockId)
 {
  //    
  if (empty($intOrderId) || empty($intLockId)) {
   return false;
  }

  //  Redis  
  $objRedisConn = self::getRedisConn();

  //  Redis key
  $strKey = sprintf(self::REDIS_LOCK_KEY_TEMPLATE, $intOrderId);

  //  Redis key   【  lock id】 【         】      ,            ,           
  $objRedisConn->watch($strKey);
  if ($intLockId == $objRedisConn->get($strKey)) {
   $objRedisConn->multi()->del($strKey)->exec();
   return true;
  }
  $objRedisConn->unwatch();
  return false;
 }

 /**
  * Redis  :IP
  */
 const REDIS_CONFIG_HOST = '127.0.0.1';

 /**
  * Redis  :  
  */
 const REDIS_CONFIG_PORT = 6379;

 /**
  *   Redis  (    ,      )
  * @param string $strIp IP
  * @param int $intPort   
  * @return object Redis  
  */
 public static function getRedisConn($strIp = self::REDIS_CONFIG_HOST, $intPort = self::REDIS_CONFIG_PORT)
 {
  $objRedis = new Redis();
  $objRedis->connect($strIp, $intPort);
  return $objRedis;
 }

 /**
  *         ID redis key
  */
 const REDIS_LOCK_UNIQUE_ID_KEY = 'lock_unique_id';

 /**
  *      ID(  Redis incr        ,     、   、  、     、      ,        ID)
  * @return mixed
  */
 public static function generateUniqueLockId()
 {
  return self::getRedisConn()->incr(self::REDIS_LOCK_UNIQUE_ID_KEY);
 }
}

//test
$res1 = Lock_Service::addLock('666666');
var_dump($res1);//  lock id,    
$res2 = Lock_Service::addLock('666666');
var_dump($res2);//false,    
$res3 = Lock_Service::releaseLock('666666', $res1);
var_dump($res3);//true,    
$res4 = Lock_Service::releaseLock('666666', $res1);
var_dump($res4);//false,    
이상 이 바로 이번에 여러분 께 정리 해 드 린 모든 내용 입 니 다.저희 에 대한 성원 에 감 사 드 립 니 다.

좋은 웹페이지 즐겨찾기