gitee项目:redis-lock-starter
核心知识清单
- @AspectJ切面编程
- redis分布式锁
- 自定义注解的使用
- spring.factories编写starter应用
- redisson加锁api
注意点
-
业务内校验不可少
- 分布式锁控制竞争时只有一个能进入方法,方法一旦结束就会释放锁,并不持久化资源的占用情况。
- 演示示例:https://gitee.com/fantasyzsp/redis-lock-starter/blob/master/src/test/java/com/sishu/redis/lock/redisson/business/AnnotatedController.java#L47-51
-
保证锁切面/或拦截器优先级高于Spring事务
-
在事务开始前就抢占锁独占资源,避免事务内受隔离性质影响使得业务内查DB的校验不生效。
-
演示示例:
- com.sishu.redis.test.AnnotatedControllerTest#insertWithUniqueAge,注意查看方法链里拦截器的顺序。
- org.springframework.aop.framework.CglibAopProxy.DynamicAdvisedInterceptor#intercept这里查看目标方法拦截器链顺序。
-
-
锁特性:可重入排它锁
- 业务内开启多线程时,需要注意锁的排他性。
- 解锁时,需要注意不要破坏重入记录(减次数,而不是直接删除key)。
- 看需求考虑后期追加更多锁特性进去。比如不可重入,公平与非公平,读写锁等。
拓展
-
redis分布式锁注意点
-
有效期设定不足导致的问题
-
多个线程同时进入临界区。(T1未执行完毕,锁失效了导致等待的T2也进入临界区),T1可能释放掉T2的锁,恶化锁对并发的控制。
-
解决方案
-
按照业务,设定合理的有效期。
-
定时延长锁有效期(redisson采用异步线程定时刷新TTL)。
-
-
-
加锁原子性问题
- 使用 set key value px milliseconds nx 命令,在加锁的同时设定有效期。(redisson几乎所有操作都用到了lua脚本实现,具有原子性)。
-
批量加锁时按一致的顺序加锁防死锁
- 将key排序后在进行加锁,同时建议解锁时逆序解锁,尽可能保证线程能够一次性获取同一组资源。
- 示例:
- T1加锁ABCDE五个资源,T2加锁ABCDE五个资源,对A的竞争只有一个能成功,只要竞争A成功,就可以获取同组资源。
- T1加锁ABCDE五个资源,T2加锁BCDEF五个资源,T3加锁CDEFG。不排序时,可能导致T1持有B,T2持有C,T3持有D,谁都无法加锁成功。
- 示例:
- 将key排序后在进行加锁,同时建议解锁时逆序解锁,尽可能保证线程能够一次性获取同一组资源。
-
加锁客户机突然宕机导致死锁问题
- 解决方案
- 常规的try finally范式无法解决,只有设定较长的过期时间。
- 不设定过期时间,异常出现时相关资源的访问一直有问题,需要人肉处理。
- 解决方案
-
redis服务器宕机
- 可以参考这篇文章的讲解 https://crazyfzw.github.io/2019/04/15/distributed-locks-with-redis/
-