1.分布式锁注意的问题
- 保证redis命令的原子性操作,即过期时间和key的设置要在一个原子操作范围内,常见就是setnx key value EX NX
- 在保证原子性的同时,要对key设置过期时间,防止程序某个环节挂了,导致锁未被释放的问题。
- 一个线程执行完操作删除key时,要保证是同一个线程删除同一个key,即使用value做唯一的id,根据value进行删除,防止锁被其他线程删除。
- 有可能出现在锁过期后都没有执行完当前线程的操作,这就需要实现锁过期时间的延续,即当前拥有锁的线程会有一个监视器,会以当前设置的key的过期时间的1/3时间进行key的过期时间的延长,,直到当前线程执行完成。这里主要使用Redisson的RedLock实现,里面自动封装了分布式锁的具体操作。(这里就不需要使用set了,直接对key进行lock和unlock)
- 在解决以上问题后,后面可能就要考虑分布式锁的性能问题了,由于使用分布式锁是让多个并发线程微观上一个一个执行,且redis执行基本命令是单线程的,所以我们可以对同一商品进行在redis中的划分,比如10000个商品供用户抢,我们原先通过一个key来锁10000,效率自然不是很高,我们可以在redis中通过划分,划分10个key,每个key对应1000个请求,这样十个线程可以一起并发,相当于有了十个锁。有十个队列在进行,性能会比一个队列多了10倍左右。原理类似于currentHashmap的分段锁。
- 上述是在单一redis服务器时会面临的问题,对于集群,则会出现新的问题,比如,当master上的key已经加锁,此时还未同步到slave,master挂了,这时就会造成锁丢失了,新的master会重新获取锁,这样就重复获取锁了,解决办法还是利用Redisson的redLock的集群方式解决。
2.实现操作
a.spring boot redisTemplate解决(单一)
(1)自定义redisTemplate
1 | import com.fasterxml.jackson.annotation.JsonAutoDetect; |
(2)分布式锁实现
1 | import org.springframework.beans.factory.annotation.Autowired; |
b.jdeis解决(单一)
1 | import org.springframework.util.StringUtils; |
c.redisson解决(集群)
1 | import org.redisson.api.RLock; |
3.依赖导入
1 | <!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-web --> |
参考:
https://juejin.cn/post/6844903830442737671
https://mp.weixin.qq.com/s/8uhYult2h_YUHT7q7YCKYQ
https://www.cnblogs.com/zhili/p/redisdistributelock.html
https://www.cnblogs.com/zhili/p/redLock_DistributedLock.html