이 글에서는 주로 PHP+redis로 구현된 비관적 잠금 메커니즘을 소개하고, Redis 잠금 메커니즘, 낙관적 잠금, 비관적 잠금 및 기타 개념을 간략하게 소개하고, php+redis로 구현된 비관적 잠금의 관련 운영 기술을 예제 형식으로 분석합니다. . 도움이 필요한 친구
를 참고하세요. 이 글의 예시는 PHP+redis에서 구현한 비관적 잠금을 설명합니다. 참고를 위해 모든 사람과 공유합니다. 세부 사항은 다음과 같습니다.
잠금 메커니즘
일반적으로 사용되는 잠금은 낙관적 잠금과 비관적 잠금으로 구분되며 이 기사의 배경 지식으로 이 두 잠금을 간략하게 소개하겠습니다. .이런 종류의 지식에 대해 이미 충분히 알고 있는 학생들은 이 부분을 건너뛸 수 있습니다.
Optimistic Lock
먼저 Baidu Encyclopedia에 대한 설명을 살펴보겠습니다. 대부분은 데이터 버전(Version) 기록 메커니즘을 기반으로 합니다. 데이터 버전이란 무엇입니까? 이는 데이터에 버전 식별자를 추가하는 것입니다. 데이터베이스 테이블을 기반으로 하는 버전 솔루션에서는 일반적으로 데이터베이스 테이블에 "버전" 필드를 추가하여 이를 수행합니다. 데이터를 읽을 때 이 버전 번호도 읽혀지며, 나중에 업데이트되면 이 버전 번호가 1씩 증가합니다. 이때, 제출된 데이터의 버전 데이터는 데이터베이스 테이블의 해당 레코드의 현재 버전 정보와 비교됩니다. 제출된 데이터의 버전 번호가 데이터베이스 테이블의 현재 버전 번호보다 높을 경우, 업데이트되지 않으면 만료된 데이터로 간주됩니다.
사실 직설적으로 말하면 체육관에 런닝머신이 하나밖에 없고 체육관 입구에 번호 매기기 기계가 있는 것과 같습니다. 체육관에 들어가는 사람은 누구나 들어가기 전에 번호를 받아야 합니다. 런닝머신에 있는 사람의 숫자가 런닝머신에 표시되어 있는 숫자(마지막으로 런닝머신을 사용한 숫자)가 작은지 확인하세요. 더 작으면 사용할 수 있고, 그렇지 않으면 번호를 전달하는 것을 의미하며 실제로 우리는 나가거나 재정렬해야 한다는 것을 알고 있지만 대기열에 뛰어들 수는 없습니다. 시스템에서는 일반적으로 오류가 반환됩니다.
비관적 잠금
마찬가지로 바이두 백과사전의 설명을 살펴보겠습니다. 강력한 배타성과 배타성을 가지고 있습니다. 이는 외부 세계에 의해 수정되는 데이터(시스템의 다른 현재 트랜잭션 및 외부 시스템에서 처리되는 트랜잭션 포함)에 대해 보수적인 태도를 의미합니다. 따라서 전체 데이터 처리 과정에서 데이터는 잠금 상태로 유지됩니다. 비관적 잠금의 구현은 데이터베이스에서 제공하는 잠금 메커니즘에 의존하는 경우가 많습니다(데이터베이스 계층에서 제공하는 잠금 메커니즘만이 데이터 액세스의 배타성을 실제로 보장할 수 있습니다. 그렇지 않으면 이 시스템에 잠금 메커니즘이 구현되어 있어도 잠금 메커니즘이 없습니다). 외부 시스템이 데이터를 수정하지 않도록 보장합니다.
그러면 같은 인기 있는 설명으로는 체육관입니다. 이번에는 문앞에 줄을 서야 하는 기계가 필요하지 않고 대신 열쇠가 있습니다(한개만). 들어가고 싶은 사람은 이 열쇠를 받아야 합니다. 물. 그가 나와서 열쇠를 벽에 걸 때까지 계속 달릴 수 있고, 다음 열쇠를 위해 싸울 수 있으며, 열쇠를 얻은 후에야 다시 들어갈 수 있습니다. 약간 비인간적으로 들리므로 비관적 잠금은 Strong Consistency 시나리오에 더 적합하지만 효율성이 상대적으로 낮고 특히 읽기 동시성이 낮습니다. 낙관적 잠금은 읽기가 많고 쓰기가 적으며 동시성 충돌이 적은 시나리오에 적합합니다.
Background
먼저 비관적 잠금이 사용되는 이유와 잠금의 세부 설계에 대해 모두가 이해할 수 있도록 이 문서의 개발 배경에 대해 이야기하겠습니다.
작업 분배 시스템: 작업 풀(mysql)에는 이제 사용자가 편집을 도와야 하는 많은 작업(기사)이 있습니다. 시스템의 기본 요구 사항은 다음과 같습니다(간소화된 버전):
1 . 사용자가 관심 있는 카테고리로 작업을 푸시하여 편집하세요.
2. 사용자가 작업을 편집하고 제출하면 다음 작업이 자동으로 푸시됩니다.
3.
4. 사용자가 일정 시간 이상 작업을 차지하면 작업 풀에 들어가 재활용됩니다. 두 가지 목표는 다음과 같습니다.
2. 즉, 사용자가 오랫동안 사용하고 해제할 수 없는 작업을 피합니다.
아이디어 많은 양의 시스템 동시성과 빈번한 쓰기 작업으로 인해 비관적 잠금은 동시에 한 명의 사용자만 각 작업을 요청할 수 있도록 제어하기 위해 선택되었습니다. 주요 아이디어는 다음과 같습니다. 1. 작업 풀에서 할당 가능한 작업을 찾습니다.2. 후보 푸시 작업으로 특정 순서에 따라 작업을 선택합니다. . 잠금에 성공하면 작업을 사용자에게 푸시하고 해당 작업 상태 및 사용자 상태를 수정합니다. 5. 잠금에 실패하면 푸시가 성공할 때까지 2~5를 반복합니다.
실현여기에서는 잠금 구현 메커니즘만 소개하고 나머지 비즈니스 로직은 건너뜁니다. 잠금 프로세스는 종종 원자 작업이라고 불리는 분해되어서는 안 되기 때문에 redis의 setnx 작업이 잠금 방법으로 선택됩니다.
코드의 단순화된 버전은 다음과 같습니다.
function lock($strMutex, $intTimeout) { $objRedis = new Redis(); //使用setnx原子型操作加锁 $intRet = $objRedis->setnx($strMutex, 1); if ($intRet) { //设置过期时间,防止死任务的出现 $objRedis->expire($strMutex, $intTimeout); return true; } return false; }
이 코드에는 문제가 있습니다. 즉, setnx는 성공하지만 만료는 실패하여 작업 중단으로 이어질 수 있습니다. 이 문제를 해결하는 일반적인 방법은 다음과 같이 setnx 대신 incr 메서드를 사용하는 것입니다.
function lock($strMutex, $intTimeout, $intMaxTimes = 0) { $objRedis = new Redis(); //使用incr原子型操作加锁 $intRet = $objRedis->incr($strMutex); if ($intRet === 1) { //设置过期时间,防止死任务的出现 $objRedis->expire($strMutex, $intTimeout); return true; } if ($intMaxTimes > 0 && $intRet >= $intMaxTimes && $objRedis->ttl($strMutex) === -1) { //当设置了最大加锁次数时,如果尝试加锁次数大于最大加锁次数并且无过期时间则强制解锁 $objRedis->del($strMutex); } return false; }
이 코드는 $intMaxTimes를 사용하여 만료에 실패하더라도 강제로 잠금 해제할 수 있도록 보장합니다. 시스템 죽은 임무는 없을 것입니다.
더 좋은 방법이 있나요?
사실 redis의 설정 작업은 setnx와 호환되며 만료 시간 설정을 지원합니다.
function lock($strMutex, $intTimeout) { $objRedis = new Redis(); //使用setnx操作加锁,同时设置过期时间 $strRet = $objRedis->set($strMutex, 1, 'ex', $intTimeout, 'nx'); if ($strRet === 'OK') { return true; } return false; }
현재로서는 이 방식이 가장 좋다고 생각하는데 왜 incr 방식을 먼저 도입하지 않고 직접 도입하지 않았나요? 사실 주의 깊은 학생들은 위의 측면에서 "일반"이라는 두 개의 굵은 단어가 있음을 알 수 있습니다. 이렇게 말하는 이유는 set 메소드가 redis2.6.12 버전의 여러 매개변수만 지원하기 때문입니다.
레벨이 제한되어 있으니 수정 환영합니다~
위 내용은 모두의 학습에 도움이 되기를 바랍니다. 더 많은 관련 내용은 PHP 중국어 홈페이지를 주목해주세요!
관련 권장 사항:
PHP를 사용하여 실제 IP를 얻는 방법 사용자 클라이언트
위 내용은 PHP와 Redis로 구현된 비관적 잠금 메커니즘 분석의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!