分布式锁背景
在秒杀业务中,有一个需求是每个用户只能下一单,这时为了阻止用户在短时间内用多个线程调用秒杀接口,我们需要用悲观锁把用户的id锁住,以当用户第一次调用这个接口的时候,它会把秒杀下单过程锁住,之后这个用户请求这个秒杀接口的线程不会在往下进行。这里我们用了synchronized关键字将用户的id锁住了。
但是当我们启动多个后端服务时,当这个用户再多次访问秒杀接口,就造成了不同的tomcat(由于负载均衡)同时处理这名用户的秒杀下单请求,这是synchronized就不会对另一台tomcat的线程起作用了,即它只负责本机的线程安全。(废话:每个服务都有自己的tomcat,没台tomcat都有自己的JVM环境,都是单独的锁监视器)所以我们需要跨JVM的锁,即跨进程的锁。
分布式锁:满足分布式系统或集群模式下多进程可见并且互斥的锁,需要满足高可用,高性能等等。
分布式锁的实现,常见的3种:
基于Redis的分布式锁
实现分布式锁时需要实现的两个基本方法:
-
获取锁:
-
互斥:确保只能有一个线程获取锁
-
非阻塞:尝试一次,成功返回true,失败返回false
-
# 添加锁,NX是互斥,EX是设置超时时间 # 这里把它们写到一个语句中,是需要用到redis的原子性,保证即使redis宕机,锁也能被释放 SET local thread1 NX EX 10 -
释放锁:
-
手动释放
-
超时释放:获取锁时添加一个超时时间
基于setnx实现的分布式锁存在的问题
- 不可重入:同一线程无法多次获取同一把锁
- 不可重试:获取锁只尝试一次就返回false,没有重试机制
- 超时释放:有可能业务执行耗时较长,也会导致锁释放,存在安全隐患
- 主从一致性:如果redis提供了主从集群,主从同步存在延迟
Redisson
Redisson是一个在redis基础上实现的Java驻内存数据网格(In-Memory Data Grid). 它不仅提供了一系列的分布式的Java常用对象,还提供了许多分布式服务,其中就包含了各种分布式锁的实现。利用Redisson的分布式锁和同步器可以解决上述手写setnx分布式锁的问题。
总结
懒得敲字了,总结一下吧: