1. 案例 有甲乙两个公司,甲选择一批自己的订单(一个订单有多个货物),生成的6位数字验证码,乙通过验证码接收订单,接收后需要将订单及订单中的货物的操作权限归属乙,甲不再拥有操作权限,要保证订单和货物的权限必须同时更改成功,我选择了开启了事务。在程序中我首先校验验证码,然后去redis查询对应内容,如果验证码正确则进行更改订单及货物的权限,然后移除Redis中的验证码。 @Transactionalpublic String receiveOrder(String code){ //第一步 校验验证码 ...... //第二步 获取验证码对应的内容 ...... //第三步 执行对应的更改权限逻辑 ...... //第四步 移除验证码 ...... //返回结果信息 return .....;}在进行测试的时候发现有问题,第一种情况,有时候可以执行成功,有时候失败,于是直接打印日志,发现失败的时候获取的内容是空的,实际是存在的;第二种,执行成功后还能继续执行成功,验证码没有被移除掉。 经过查询相关信息发现了问题的原因竟然是因为事务导致的,是因为当开启事务的时候,Redis将进行队列模式执行,Redis会按照队列顺序进行执行,会进行等待。 2. 具体原因:Redis为单进程单线程模式,采用队列模式将并发访问变成串行访问,Redis对事物支持不会很复杂,当一个客服端连接Redis服务时,发出了MULTI命令时,这个连接会进入事物,在执行MULTI命令之后,执行所有的命令都不会执行,会先放到一个队列中,会提示正在Query,当最后执行EXEC命令之后,Redis会按照之前的进入队列的顺序,执行命令。 3. 解决方案解决办法是利用了Spring事务代理的特性将Redis执行的部分抽离出来,导致Redis部分事务失效,这样也能保证权限的问题。
@Transactionalpublic String receiveOrder(String code){ //第一步 校验验证码 ...... //第二步 获取验证码对应的内容 String result = getCodeContent(code); //第三步 执行对应的更改权限逻辑 ...... //第四步 移除验证码 if(!removeCode(code)){ //如果移除失败 触发事务回滚 } //返回结果信息 return .....;}//获取验证码内容private String getCodeContent(String code){ //获取验证码对应的内容 ...... //返回结果信息 return .....;}
|