Points to note when using redis distributed lock + transaction + AOP together

problem

The distributed lock is used in the project, and then considering that there are multiple business interfaces that need to add distributed locks, so, the distributed locks are placed in aop for processing, and realized by surrounding notifications, but when used, There is a problem:
the aspect I added is like this

@Component
@Aspect
public class RedisLockAspect {
    
    

    @Around("@annotation(com.test.RedisLock)")
    public Object lockRedisLock(ProceedingJoinPoint pjp) throws Throwable {
    
    
        1.获取自定义注解中指定的RedisKey,获取到指定的锁超时时间等参数
        2.加锁
        3.加锁失败的话,return
        4.调用业务代码 pjp.proceed();
        5.释放锁
    }
}

Here is the pseudo code, where com.test.RedisLock is a custom annotation that I have customized. Before locking, I will try to get the lock information configured in the custom annotation; in
this case, which interface I need to lock, I only need to On the interface, just add @RedisLock annotation

The problem is this:
when the external system calls the orderCreate() interface, due to network reasons, the interface response time has timed out. As a result, the upstream business system retryed dubbo and called the creation interface again, which led to my problem. In the system, there are two identical order information;
after checking the code, there is an idempotent check based on the upstream business order number in the interface; that is to say: when the second call is made, the idempotent check is not Effective, idempotence is not effective, it may be because the transaction has not yet been committed
figure 1

figure 2

According to my thoughts, the system should be like this; but actually checked the log and found a problem. When the code is executed, it is not locked first, and then the transaction is opened; but the other way around,
image 3
this model has problems. Up:

  1. When the interface was called for the first time, it timed out, but the business code was still executing
  2. Immediately after the dubbo retry mechanism, the second interface call was initiated. When the call was made, the transaction was opened first, and the interface tried to lock. The problem is here. When I try to lock the second time, the first The second call has been executed, the lock is released, but the transaction has not yet been committed
  3. The second call to lock is successful, the business code is executed, and the idempotent judgment is performed in the business code. Since the first transaction has not been submitted at this time, there is no upstream business order number information in the database, so the second time The idempotent check is passed, and then other business operations such as order creation
  4. At this time, the first interface call submitted the transaction; after the second call was executed, the transaction was also submitted

The reason why the execution order of the aspects corresponding to the transaction and the Redis distributed lock is out of order is that one point is overlooked: the bottom layer of the
aspect priority
spring transaction is also implemented through AOP, so the issue of aspect priority is involved. From the log Analysis, in the case that the priority is not specified, the priority of the aspect of the transaction is high, and the priority of the aspect I defined to handle distributed locks is low; if you know the problem, just debug to verify it.
Figure 4
From the figure, you can see It is indeed that the transaction is executed first, and after the transaction is opened, the locking logic is called again.

Solution

There are two solutions:
1. In the aspect of dealing with Redis distributed locks, specify the highest priority
2. Reduce the scope of the transaction, put the logic of idempotent verification outside the transaction, and other non-essential after the order is created The business logic is placed outside the transaction;
for the first solution, you only need to add this to the aspect of dealing with distributed locks.

@Order(Ordered.HIGHEST_PRECEDENCE)

Because when aop processes aspects, if there are multiple aspects, the priority will be determined according to the size of the order, the smaller the value, the higher the priority
Insert picture description here

After adding this annotation, it is the model of the second picture in the note

  1. Add distributed lock
  2. Open transaction
  3. Execute business code
  4. Commit transaction
  5. Release lock

So, just one sentence: Let the priority of the distributed lock aspect be higher than the transaction.

Guess you like

Origin blog.csdn.net/CPLASF_/article/details/108931549