一、乐观锁概述
乐观锁是一种并发控制机制,它假定在数据变更时不会有冲突发生,因此不会像悲观锁一样在操作时先加锁。
在Redis中,乐观锁常用于多线程、多用户同时操作同一个数据的场景,例如秒杀、抢购、投票等。
二、Redis实现乐观锁的常用方法
1. WATCH/MULTI/EXEC
Redis通过 WATCH/MULTI/EXEC 指令实现乐观锁。这种方式基于事务,所以必须使用 Redis 2.0.0 版本以上才能使用。它的具体实现步骤如下:
// PHP代码示例 $redis = new Redis(); $redis->connect('127.0.0.1', 6379); $key = 'goods_1001'; $quantity = 10; $redis->watch($key); // 监听商品库存 $currentQuantity = $redis->get($key); if ($currentQuantity < $quantity) { // 判断库存是否充足 $redis->unwatch(); // 取消监听 echo '库存不足'; } else { $redis->multi(); // 开启事务 $redis->decrby($key, $quantity); // 减少商品库存 $redis->exec(); // 提交事务 echo '扣减成功'; }
2. Redis分布式锁
分布式锁基于SETNX(SET if Not eXists)指令实现。使用 SETNX 可以在Redis中创建一个不存在的Key,如果该Key已存在,则设置失败,返回0,设置成功返回1。
在使用Redis分布式锁时,应该注意以下问题:
(1)由于每个客户端都是独立的,所以在执行一个操作时可能会有多个客户端同时重复执行。我们需要解决相互之间的干扰。
(2)如果Redis分布式锁的Key在SETNX后会因某种原因过期,那么仍然存在多个进程同时竞争锁的情况。
// PHP代码示例 $redis = new Redis(); $redis->connect('127.0.0.1', 6379); $key = 'lock_key'; $value = uniqid(); // 生成唯一值作为锁的值 $expire = 10; // 锁过期时间,单位为秒 if ($redis->setnx($key, $value)) { // SETNX创建Key $redis->expire($key, $expire); // 设置Key过期时间 echo '加锁成功'; // TODO:执行业务逻辑 $redis->del($key); // 释放锁 } else { echo '加锁失败'; }
三、实战应用:Redis实现秒杀系统
下面结合Redis实现秒杀系统,以更形象地认识乐观锁。
假设有100个商品,每个用户最多能够购买3个,当库存不足时不允许继续购买。
系统的实现思路如下:
(1)使用 Redis 分布式锁保证操作的同步。
(2)在 Redis 中存储每个用户的购买次数。
(3)通过 Redis 事务,先锁定 Redis 中的库存和用户购买次数,再判断库存和用户购买次数是否充足,若充足扣减库存和购买次数。
// PHP代码示例 $redis = new Redis(); $redis->connect('127.0.0.1', 6379); $key = 'goods_1001'; $quantity = 10; $user_id = 101; $user_key = "user_{$user_id}"; $user_limit = 3; $lock_key = 'lock_key'; $lock_expire = 10; // 获取分布式锁 while (!$redis->setnx($lock_key, uniqid())) { usleep(1000); // 暂停1ms再次尝试 } $redis->expire($lock_key, $lock_expire); // 判断商品库存是否充足 $redis->watch($key, $user_key); $currentQuantity = $redis->get($key); $currentUserLimit = $redis->get($user_key) ?? 0; if ($currentQuantity >= $quantity && $currentUserLimit < $user_limit) { // 开启Redis事务 $redis->multi(); $redis->incrby($user_key, 1); // 记录用户购买次数+1 $redis->decrby($key, $quantity); // 商品库存-10 // 执行Redis事务 $res = $redis->exec(); if ($res) { echo '购买成功'; } else { echo '购买失败'; } } else { echo '购买失败,库存不足或购买次数已达上限'; } $redis->del($lock_key); // 释放分布式锁
总结
本文详细介绍了Redis乐观锁的实现方式,其中包括使用 Redis 事务和 Redis 分布式锁两种方式。在实际应用中,需要根据具体业务场景选择合适的方式来实现乐观锁。希望能对读者有所帮助。