当前位置: 首页> 汽车> 时评 > 关键词首页排名代做_网络设计的专业有哪些_志鸿优化设计电子版_新东方英语线下培训学校

关键词首页排名代做_网络设计的专业有哪些_志鸿优化设计电子版_新东方英语线下培训学校

时间:2025/7/11 0:32:58来源:https://blog.csdn.net/m0_68711597/article/details/146335587 浏览次数: 1次
关键词首页排名代做_网络设计的专业有哪些_志鸿优化设计电子版_新东方英语线下培训学校

上一篇文章,我详细解释了redis实现商品秒杀防止超卖https://blog.csdn.net/m0_68711597/article/details/146313029?spm=1001.2014.3001.5501

上一篇文章中 我使用原子命令结合lua脚本似乎就已经避免了并发 那问题来了  为啥还需要用锁?这俩有啥区别呢???

Lua 脚本在 Redis 中的原子性与锁的区别主要体现在以下方面:


1. Lua 脚本的原子性原理

  • 单线程模型:Redis 使用单线程处理命令,所有命令按顺序执行。Lua 脚本会被视为一个整体任务,执行期间不会被其他命令中断。
  • 原子性保证:脚本内的所有操作(如 GETDECRRPUSH)会连续执行,中间不会有其他客户端操作插入。这保证了脚本的原子性。
  • 无需锁机制:由于 Redis 的单线程特性,Lua 脚本天然避免了并发冲突,无需额外加锁。

2. 锁的机制

  • 显式控制:锁需要手动获取(如 SETNX)和释放,确保临界区代码在同一时间只被一个客户端执行。
  • 适用场景:适用于分布式系统或多实例环境,协调多个客户端/服务对共享资源的访问。
  • 潜在问题:死锁、锁超时、竞争条件等需要额外处理。

3. 关键区别

特性Lua 脚本
原子性保证由 Redis 单线程模型保证,无需额外操作。需要显式加锁/解锁,且依赖锁的正确实现。
性能开销无锁竞争,性能更高。锁的获取/释放可能带来延迟和竞争开销。
适用场景单 Redis 实例内的原子操作。分布式系统或多实例间的资源协调。
复杂度脚本内实现原子操作,逻辑简单。需处理锁超时、死锁等问题,复杂度较高。

4. 示例对比

使用 Lua 脚本扣减库存
 

lua

-- 原子性由 Redis 单线程保证,无需锁
local stock = tonumber(redis.call('GET', 'stock')) or 0
if stock > 0 thenredis.call('DECR', 'stock')return 1 -- 成功
end
return 0 -- 失败
使用锁扣减库存
 

python

# 需要显式加锁,处理锁超时和异常
lock = redis.lock('stock_lock', timeout=10)
try:if lock.acquire():stock = redis.get('stock')if stock > 0:redis.decr('stock')return 1  # 成功return 0  # 失败
finally:lock.release()

5. 总结

  • Lua 脚本:适合在单 Redis 实例中实现原子操作,简单高效,无锁开销。
  • :适合分布式环境或跨实例资源协调,但需处理锁的复杂性。

在 Redis 中优先使用 Lua 脚本实现原子操作,除非需要在分布式系统中跨节点协调资源。

尝试封装一个锁工具类  这里我用的php语法,不用细扣语法,单纯参考思路即可

6.代码实例-redis-lock-util

 这里是php  不同语言对redis的封装是不一样的  但是逻辑和思路是一致的 重在思路:

<?php/*** @author 胖虎爱java* @date 2025/3/18$ 09:26$**/
class base_lib_RedisLock
{private $redis;public function __construct(){$SRedis = new SRedis();$SRedis->init();$this->redis = $SRedis->getRedisInstance();
//创建连接对象 在类初始化的时候}public function lock($lock_name, $acquire_timeout, $lock_timeout){
//这三个参数分别是锁名  持有锁的时间  以及锁的超时自动销毁时间$uuid = md5(time() . bin2hex(openssl_random_pseudo_bytes(16)));$lock_timeout = intval(ceil($lock_timeout));$lock_end_time = time() + $acquire_timeout;
//这里采用乐观锁  取了一个uuid作为唯一标识区分不同的请求  while (time() < $lock_end_time) {$lua = <<<LUA
local result = redis.call("setnx", KEYS[1], ARGV[1])
if result == 1 thenredis.call("expire", KEYS[1], ARGV[2])return 1
elseif redis.call("ttl", KEYS[1]) == -1 thenredis.call("expire", KEYS[1], ARGV[2])return 0
end
return 0
LUA;
//使用lua命令 不会自己去学$result = $this->redis->eval($lua, [$lock_name, $uuid, $lock_timeout], 1);if ($result == 1) {return $uuid;}
//每次重试间隔10毫秒usleep(10000); // 等待10毫秒后重试}return false;}public function unlock($lock_name, $uuid){
//释放锁   通过唯一标识做区分 不然释放错了就惨了$lua = <<<LUA
if redis.call("get", KEYS[1]) == ARGV[1] thenreturn redis.call("del", KEYS[1])
elsereturn 0
end
LUA;$this->redis->eval($lua, [$lock_name, $uuid], 1);}
}

封装好后

在控制器内进行处理 具体的处理思路如下

不同语言对redis的封装是不一样的  但是逻辑和思路是一致的 重在思路:

不懂可以看  上一篇文章,我详细解释了redis实现商品秒杀防止超卖

7.秒杀和防止超卖的实现

<?phpclass  controller_product extends components_sbasepage
{public function __construct($need_login = false){parent::__construct($need_login);}//    public function pageIndex($inPath)
//    {
//    会出现超卖的错误代码示例
//        $path_data = base_lib_BaseUtils::sstripslashes($this->getUrlParams($inPath));
//        $user_id = base_lib_BaseUtils::getStr($path_data['user_id']);
//           $SRedis=new SRedis();
//           $SRedis->init();
//           $redis= $SRedis->getRedisInstance();
//            if ($redis->get("stock:10")!=0)
//            {
//            $redis->set("stock:10",$redis->get("stock:10")-1);
//                if ($redis->lLen("miaosha_user")<10){
//                    $redis->rPush("miaosha_user",$user_id);
//                    return base_lib_Return::REDataJson(0,"秒杀成功");
//                }else{
//                    return base_lib_Return::REDataJson(0,"很抱歉秒杀失败");
//                }
//            }
//}//redis原子命令实现public function pageIndex($inPath) {$path_data = base_lib_BaseUtils::sstripslashes($this->getUrlParams($inPath));$user_id = base_lib_BaseUtils::getStr($path_data['user_id']);$SRedis = new SRedis();$SRedis->init();$redis = $SRedis->getRedisInstance();//lua脚本 原子命令$lua = <<<LUA
local stock_key = KEYS[1]
local list_key = KEYS[2]
local user_id = ARGV[1]-- 检查库存
local stock = tonumber(redis.call('GET', stock_key) or 0)
if stock <= 0 then return 0 end-- 扣减库存
local remaining = redis.call('DECR', stock_key)
if remaining < 0 thenredis.call('INCR', stock_key)return 0
end-- 加入用户列表
redis.call('RPUSH', list_key, user_id)
return 1
LUA;// 执行脚本(KEYS[1], KEYS[2], ARGV[1])$result = $redis->eval($lua, ["stock:10", "miaosha_user", $user_id], 2);if ($result === 1) {return base_lib_Return::REDataJson(0, "秒杀成功");} else {return base_lib_Return::REDataJson(0, "很抱歉,秒杀失败");}}//分布式锁实现public function pageIndex2($inPath){$path_data = base_lib_BaseUtils::sstripslashes($this->getUrlParams($inPath));$user_id = base_lib_BaseUtils::getStr($path_data['user_id']);$redis = new base_lib_RedisLock();$result = false;try {// 尝试10秒内获取锁,锁有效期5秒$result = $redis->lock("stock", 1, 5);if (!$result) {return base_lib_Return::REDataJson(0, "活动太火爆,请稍后重试");}// 复用Redis连接$redisutil = new  base_lib_Cache("redis");// 原子操作:检查库存并记录用户$lua = <<<LUAlocal stock = redis.call                                                                                                                                        ("get", KEYS[1])if tonumber(stock) > 0 thenredis.call("decr", KEYS[1])redis.call("rpush", KEYS[2], ARGV[1])return 1endreturn 0
LUA;$success = $redisutil->redis_eval($lua, ["stock:10", "miaosha_user", $user_id], 2);if (!$success) {return base_lib_Return::REDataJson(0, "库存不足");}return base_lib_Return::REDataJson(1, "秒杀成功");} catch (Exception $e) {error_log("秒杀异常[user_id=$user_id]: " . $e->getMessage());return base_lib_Return::REDataJson(0, "系统繁忙,请重试");} finally {if ($result !== false) {$redis->unlock("stock", $result);}}}}

8.前端调用示例 js并发多线程

<html>
<meta charset="utf-8">
<body>
<textarea style="width: 500px;height: 500px;font-size: 20px" id="box"></textarea>
<script>// 生成20个随机用户ID(范围:10000-99999)const generateRandomUserIds = () => {const ids = new Set();while (ids.size < 20) {const timestamp = Date.now().toString().slice(-5); // 取时间戳后5位const randomPart = Math.floor(Math.random() * 9000) + 1000; // 4位随机数ids.add(`${timestamp}${randomPart}`);}return Array.from(ids);};// 修改后的请求函数function readysell(userId) {fetch(`/product/index2?user_id=${userId}`, {method: 'POST',headers: { 'Accept': 'application/json' }}).then(response => {if (!response.ok) throw new Error('请求失败');return response.json();}).then(data => {const logMsg = data.data ? `用户 ${userId} 获得锁` : `用户 ${userId} 请求繁忙`;document.getElementById('box').append(logMsg + '\n')console.log(logMsg);}).catch(error => console.error(error));}// 并发模拟逻辑(携带20个随机ID)function simulateConcurrency() {const userIds = generateRandomUserIds();userIds.forEach((userId, index) => {setTimeout(() => {console.log(`线程 ${index + 1} 使用ID: ${userId}`);readysell(userId);}, Math.random() * 500); // 随机延迟});}// 启动并发测试simulateConcurrency();
</script>
</body>
</html>

结果:

                          可以看到防止超卖,并且实现秒杀

如果有帮助到你,是我的荣幸,感谢关注收藏

关键字:关键词首页排名代做_网络设计的专业有哪些_志鸿优化设计电子版_新东方英语线下培训学校

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com

责任编辑: