Redis加锁命令分有INCR、SETNX、SET
一、INCR锁
key不存在时,key的值会先被初始化为0,其它用户在执行INCR操作进行加一,
如果返回的数大于1,说明这个锁正在被使用当中,通常用在同时只能有一个人可以操作某个行为。
$redis->incr($key);
$redis->expire($key, $time); //过期时间
$redis->del($key); //删除锁
 
1、 客户端A请求服务器获取key的值为1表示获取了锁
2、 客户端B也去请求服务器获取key的值为2表示获取锁失败
3、 客户端A执行代码完成,删除锁
4、 客户端B在等待一段时间后在去请求的时候获取key的值为1表示获取锁成功
5、 客户端B执行代码完成,删除锁
二、SETNX锁
当key不存在时,将key设置为value,如果key已存在,则SETNX不做任何动作。
$redis->setNX($key, $value);
$redis->expire($key, $time); //过期时间
$redis->del($key); //删除锁
     
1、 客户端A请求服务器设置key的值,如果设置成功就表示加锁成功
2、 客户端B也去请求服务器设置key的值,如果返回失败,那么就代表加锁失败
3、 客户端A执行代码完成,删除锁
4、 客户端B在等待一段时间后在去请求设置key的值,设置成功
5、 客户端B执行代码完成,删除锁
三、SET锁
$redis->set($key, $value, array('nx', 'ex' => $time));  //ex表示秒
$redis->del($key); //删除锁
1、 客户端A请求服务器设置key的值,如果设置成功就表示加锁成功
2、 客户端B也去请求服务器设置key的值,如果返回失败,那么就代表加锁失败
3、 客户端A执行代码完成,删除锁
4、 客户端B在等待一段时间后在去请求设置key的值,设置成功
5、 客户端B执行代码完成,删除锁

PHP实现setnx锁
1.创建以下两张数据表

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
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

2.测试不加锁情况

$pdo = new PDO('mysql:host=127.0.0.1;dbname=demo', 'demo', 'demo');
$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);
    }
}
window系统在cmd命令,进入apache\bin目录执行 ab -n 800 -c 800 域名/  模拟800次并发请求

redis悲观锁 redis incr锁_缓存


通过以上结果看到,库存和订单都超出了

3.测试加锁情况

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=demo', 'demo', 'demo');
$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{    //加锁不成功执行其他操作。
}

执行以上相同的并发请求,结果如下

redis悲观锁 redis incr锁_缓存_02