1. 案例

有甲乙两个公司,甲选择一批自己的订单(一个订单有多个货物),生成的6位数字验证码,乙通过验证码接收订单,接收后需要将订单及订单中的货物的操作权限归属乙,甲不再拥有操作权限,要保证订单和货物的权限必须同时更改成功,我选择了开启了事务。在程序中我首先校验验证码,然后去redis查询对应内容,如果验证码正确则进行更改订单及货物的权限,然后移除Redis中的验证码。

@Transactional
public String receiveOrder(String code){

    //第一步 校验验证码
    ......

    //第二步 获取验证码对应的内容
    ......

    //第三步 执行对应的更改权限逻辑
    ......

    //第四步 移除验证码
    ......

    //返回结果信息
    return .....;
}

在进行测试的时候发现有问题,第一种情况,有时候可以执行成功,有时候失败,于是直接打印日志,发现失败的时候获取的内容是空的,实际是存在的;第二种,执行成功后还能继续执行成功,验证码没有被移除掉。

经过查询相关信息发现了问题的原因竟然是因为事务导致的,是因为当开启事务的时候,Redis将进行队列模式执行,Redis会按照队列顺序进行执行,会进行等待。

2. 具体原因:

Redis为单进程单线程模式,采用队列模式将并发访问变成串行访问,Redis对事物支持不会很复杂,当一个客服端连接Redis服务时,发出了MULTI命令时,这个连接会进入事物,在执行MULTI命令之后,执行所有的命令都不会执行,会先放到一个队列中,会提示正在Query,当最后执行EXEC命令之后,Redis会按照之前的进入队列的顺序,执行命令。

3. 解决方案

解决办法是利用了Spring事务代理的特性将Redis执行的部分抽离出来,导致Redis部分事务失效,这样也能保证权限的问题。


@Transactional public String receiveOrder(String code){ //第一步 校验验证码 ...... //第二步 获取验证码对应的内容 String result = getCodeContent(code); //第三步 执行对应的更改权限逻辑 ...... //第四步 移除验证码 if(!removeCode(code)){ //如果移除失败 触发事务回滚 } //返回结果信息 return .....; } //获取验证码内容 private String getCodeContent(String code){ //获取验证码对应的内容 ...... //返回结果信息 return .....; } //移除验证码 private boolean removeCode(String code){ //移除验证码 ...... //返回结果信息 return .....; }
最后修改:2019 年 03 月 18 日
如果觉得我的文章对你有用,请随意赞赏