Redis事务+Redis锁机制+Redis解决秒杀问题

目录

redis事务

redis事务对错误的处理

redis事务特性

redis锁

redis实现秒杀

Lua脚本


redis事务

redis事务简介:
    redis事务是一个单独的隔离操作:事务中的所有命令都会序列化、按顺序地执行。
    事务在执行的过程中,不会被其他客户端发送来的命令请求所打断。
    redis事务的主要作用就是串联多个命令防止别的命令插队。

事务命令:
    Multi、Exec、discard
    输入 Multi 命令后,输入的命令会依次进入命令队列中(这个阶段是组队阶段);直到输入
    Exec,所有输入的命令会依次执行(这个阶段是执行阶段。类似先进先出)。
    在 Multi 中,如果中途不想再继续事务(或是出现了错误),可以输入 discard 命令来取消。

redis事务对错误的处理

事务的错误处理:
    ①组队时有任意命令报告出现错误,执行时整个队列会被取消。会返回一个nil。
    ②组队时没有命令报告出现错误,执 行时发现有命令报错,则错误命令不执行,其他命令照旧执行,不会回滚。

redis的命令是原子性的,事务是非原子性的。

redis事务特性

redis 事务的三特性:
    ①单独的隔离操作:事务中的所有命令都会序列化、按顺序地执行。事务在执行的过程中,不会被其他客户端发送来的命令请求所打断。
    没有隔离级别的概念:队列中的命令没有提交之前都不会实际被执行,因为事务提交前任何指令都不会被实际执行。
    不保证原子性:事务中如果有一条命令执行失败,其后的命令仍然会被执行,没有回滚。

redis锁

事务冲突
    问题:如一个账号同时发出三笔交易请求,当金额单项不超过余额,总额超过余额时。
    解决方式:锁(悲观锁、乐观锁)

悲观锁:
    当操作一个 key 时,给它上锁,使别人不能操作,只有当自己操作完解锁后,别人才能操作 key。传统的关系型数据库里边就用到了很多这种锁机制,比如行锁、表锁等,读锁、写锁等,都是在操作之前先上锁。
    缺点:效率低。

乐观锁:
    每次拿数据都假设没有其他人修改,不去上锁,当到了更新操作,会去判断这个数据有没有在此期间被修改。
    如同时操作一个数据,都会获得当前数据的版本号,当其中一个操作成功数据后,给数据加版本号,其他人修改时,发现数据版本号不一致,操作失败。抢票就是乐观锁的应用。
    乐观锁适用于多读应用类型,这样可以提高吞吐量。redis就是利用这种 check-and-set 机制实现事务的。

    执行 mutli 之前,先执行 watch key1 [key2],可以监视一个(或多个)key,如果在事务执行之前这个(或多个)key 被其他命令改动,事务执行将不成功。

    unwatch:取消对key的所有监视。
    如果在执行 watch 命令之后,exec 命令或 discard 命令先被执行了的话,那么就不需要再执行 unwatch 了。 

redis实现秒杀

代码实现秒杀思想
秒杀案例:
在多请求多并发情况下,会出现超卖、请求超时问题、库存遗留问题。
请求超时解决方案:用连接池(通过连接池得到 jedis 对象)
超卖解决方案:使用 watch 监视操作 key ,使用事务 multi 进行组队,用 exec 执行事务。判断exec 返回的值,如果为 null 提示秒杀失败

库存遗留解决方案:用redis lua脚本实现基于缓存的分布式锁。(乐观锁造成库存遗留、连接超时问题。redis中默认不能直接使用悲观锁。)可以用LUA脚本语言解决。

Lua脚本


Lua:
    一个小巧的脚本语言,Lua脚本可以很容易的被 c/c++ 代码调用,也可以反过来调用 c/c++
    的函数,Lua 并没有提供强大的库,一个完整的 Lua 解释器不过 200k,所以 Lua 不适合作
    为开发独立应用程序的语言,而是作为嵌入式脚本语言。
    很多应用程序、游戏使用LUA作为自己的嵌入式脚本语言,以此来实现可配置性、可扩展性。
    比如魔兽世界、博德之门、愤怒的小鸟等众多游戏插件或外挂。
lua脚本 在 redis中的优势:
    可以将复杂多步骤的 redis 操作,写为一个脚本,一次提交给 redis 执行,减少反复连接
    redis 的次数,提升性能。
    lua 脚本类似 redis 事务,有一定的原子性,不会被其他命令插队,可以完成一些 redis 事务
    性的操作。
    但是注意 redis 的 lua 脚本功能,只有在 redis 2.6 以上的版本才可以使用。
    利用 lua 脚本淘汰用户,解决超卖问题。
    redis 2.6 版本以后,通过 lua 脚本解决秒杀问题,实际上是利用 redis 单线程的特性,用
    任务队列的方式解决多任务并发问题。
解决超卖、连接池超时、库存依赖问题脚本:

    local userid=keys[1];    // 传入参数
    local prodid=keys[2];
    local qtkey="sk:"..prodid.":qt";    // 拼接库存key、用户key
    local userskey="sk:"..prodid.":usr";
    # 调用redis中sismenber
    local userExists=redis.call("sismenber",userskey,userid);    
    if tonumber(userExists)==1 then    # 看对应用户是否已经买过商品
        return 2;    # 已经秒杀到商品,不能再买第二次
    end
    # 调用redis中get 方法
    local num=redis.call("get",qtkey);
    if tonumber(num)<=0 then    # 看 num 是否小于等于0
        return 0;    # 满足条件,返回0
    else
        redis.call("decr",qtkey);    # 库存减一
        redis.call("sadd",userskey,userid);    
    end
    return 1;    # 表示秒杀成功

    java 类调用:

    # 建连接池
    JedisPoll jedisPool = JedisPoolUtil.getJedisPoolInstance();
    Jedis jedis = jedisPool.getResource();
    # 调用lua脚本 LuaScript中存放lua脚本(LuaScript = "lua脚本内容")
    String shal = jedis.scriptLoad(LuaScript);
    Object result = jedis.evalsha(shal,2,userid,prodid);

  • 3
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值