높은 동시성 문제는 우리가 자주 접하는 문제인데, 높은 동시성 문제를 해결하는 방법은 무엇일까요? 이 기사에서는 높은 동시성 문제를 해결하기 위해 Redis 잠금을 사용하는 방법을 소개합니다.
여기에서는 주로 Redis의 setnx 명령을 사용하여 높은 동시성을 처리합니다.
setnx에는 두 개의 매개변수가 있습니다. 첫 번째 매개변수는 키를 나타냅니다. 두 번째 매개변수는 값을 나타냅니다. 현재 키가 존재하지 않으면 두 번째 매개변수를 값으로 사용하여 현재 키가 삽입됩니다. 1을 반환합니다. 현재 키가 존재하면 0이 반환됩니다.
재고 테이블 생성
CREATE TABLE `storage` ( `id` int(11) unsigned NOT NULL AUTO_INCREMENT, `number` int(11) DEFAULT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=latin1
초기 재고를 10으로 설정
주문 테이블 생성
CREATE TABLE `order` ( `id` int(11) unsigned NOT NULL AUTO_INCREMENT, `number` int(11) DEFAULT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=latin1
잠금 없이 테스트
$pdo = new PDO('mysql:host=127.0.0.1;dbname=test', 'root', 'root'); $sql="select `number` from storage where id=1 limit 1"; $res = $pdo->query($sql)->fetch(); $number = $res['number']; if($number>0) { $sql ="insert into `order` VALUES (null,$number)"; $order_id = $pdo->query($sql); if($order_id) { $sql="update storage set `number`=`number`-1 WHERE id=1"; $pdo->query($sql); } }
ab 시뮬레이션 동시성을 테스트하여 인벤토리가 올바른지 확인했습니다.
mysql> select * from storage; +----+--------+ | id | number | +----+--------+ | 1 | 0 | +----+--------+ 1 row in set (0.00 sec)
주문 테이블
mysql> select * from `order`; +----+--------+ | id | number | +----+--------+ | 1 | 10 | | 2 | 10 | | 3 | 9 | | 4 | 7 | | 5 | 6 | | 6 | 5 | | 7 | 5 | | 8 | 5 | | 9 | 4 | | 10 | 1 | +----+--------+ 10 rows in set (0.00 sec)
을 보니, 동일한 재고 데이터로 여러 주문이 운영되고 있어 과매도 상황이 발생할 수 있는 것으로 나타났습니다.
코드 수정 및 데이터 제어를 위한 redis lock 추가
<?php /** * Created by PhpStorm. * User: daisc * Date: 2018/7/23 * Time: 14:45 */ class Lock { private static $_instance ; private $_redis; private function __construct() { $this->_redis = new Redis(); $this->_redis ->connect('127.0.0.1'); } public static function getInstance() { if(self::$_instance instanceof self) { return self::$_instance; } return self::$_instance = new self(); } /** * @function 加锁 * @param $key 锁名称 * @param $expTime 过期时间 */ public function set($key,$expTime) { //初步加锁 $isLock = $this->_redis->setnx($key,time()+$expTime); if($isLock) { return true; } else { //加锁失败的情况下。判断锁是否已经存在,如果锁存在切已经过期,那么删除锁。进行重新加锁 $val = $this->_redis->get($key); if($val&&$val<time()) { $this->del($key); } return $this->_redis->setnx($key,time()+$expTime); } } /** * @param $key 解锁 */ public function del($key) { $this->_redis->del($key); } } $pdo = new PDO('mysql:host=127.0.0.1;dbname=test', 'root', 'root'); $lockObj = Lock::getInstance(); //判断是能加锁成功 if($lock = $lockObj->set('storage',10)) { $sql="select `number` from storage where id=1 limit 1"; $res = $pdo->query($sql)->fetch(); $number = $res['number']; if($number>0) { $sql ="insert into `order` VALUES (null,$number)"; $order_id = $pdo->query($sql); if($order_id) { $sql="update storage set `number`=`number`-1 WHERE id=1"; $pdo->query($sql); } } //解锁 $lockObj->del('storage'); } else { //加锁不成功执行其他操作。 }
다시 ab
테스트를 실시하여 테스트 결과 확인
mysql> select * from `order`; +----+--------+ | id | number | +----+--------+ | 1 | 10 | | 2 | 9 | | 3 | 8 | | 4 | 7 | | 5 | 6 | | 6 | 5 | | 7 | 4 | | 8 | 3 | | 9 | 2 | | 10 | 1 | +----+--------+ 10 rows in set (0.00 sec)
주문 테이블이 동일한 재고 데이터를 운영하지 않는 것을 발견했습니다. 따라서 Redis Lock을 사용하면 높은 동시성을 효과적으로 처리할 수 있습니다.
사실 잠금 시 만료 시간을 판단할 필요는 없습니다. 여기서는 교착 상태를 피하기 위해 만료 시간 판단을 추가합니다. 잠금이 만료되면 적극적으로 삭제하십시오.
관련 권장 사항:
php는 파일 잠금을 사용하여 높은 동시성 문제를 해결합니다.
Redis를 사용하여 동시성 일관성 문제를 해결하기 위해 PHP 세션을 저장하는 방법
위 내용은 Redis 잠금을 사용하여 높은 동시성 문제를 해결하는 방법의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!