> 백엔드 개발 > PHP 튜토리얼 > Redis 잠금을 사용하여 높은 동시성 문제를 해결하는 방법

Redis 잠금을 사용하여 높은 동시성 문제를 해결하는 방법

不言
풀어 주다: 2023-04-04 06:46:02
원래의
3378명이 탐색했습니다.

높은 동시성 문제는 우리가 자주 접하는 문제인데, 높은 동시성 문제를 해결하는 방법은 무엇일까요? 이 기사에서는 높은 동시성 문제를 해결하기 위해 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 중국어 웹사이트의 기타 관련 기사를 참조하세요!

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