目录
一. 前言
为什么需要分布式锁?
在微服务架构下.多个应用服务同时对同一条数据做修改(例如:秒杀扣减库存),为了保证数据的正确性,我们需要保证某个时间内,只可以有一个应用修改,这时候就用到了分布式锁。
二. 基于Redis实现分布式锁
为什么Redis可以实现分布式锁?
因为Redis是一个单独的非业务服务,不会受到其他服务的限制,所有的服务都可以向Redis发送请求,且只有一个服务可以写入命令成功,当这个服务写入成功时会获得锁,可以进行后续的对资源的操作,此时其他服务写入命令会失败,得不到锁。
如何实现
Redis的String类型就可以实现,没有线程安全问题。
锁的获取
setnx key value命令:表示SET if Not eXists,即如果key不存在,才会设置值为value,否则什么都不做
当多个客户端同时向Redis写入try_lock,只有一个会成功,会获取分布式锁成功,返回1
其他的客户端会失败,返回0
锁的释放
当获得锁的客户端执行完后续操作时,释放锁资源,即删除try_lock
那么此时其他客户端在次获取锁时就会成功,就可以执行后续任务
但是这样还有有新的问题:
假如客户端1在获取到锁资源后,服务宕机了,那么这个try_lock会一直存在redis中,那么其他服务就永远无法获取到锁了,形成死锁。
三. 如何避免死锁?锁的过期时间如何设置
设置键过期时间,超过这个时间即给key删除掉。
这样的话,就算当前服务获取到锁后宕机了,这个key也会在一定时间后被删除,其他服务照样可以继续获取锁。
给serverLock键设置一个10秒的过期时间,10秒后会自动删除该键。
这样虽然解决了上面说的问题,但是又会有新的问题。
假如客户端1加锁成功,锁会在10s后自动释放,但由于业务复杂,执行时间过长,10s内还没执行完,此时锁已经被redis自动释放掉了。此时客户端2就重新获取到了该锁,客户端2开始执行他的业务,客户端1在执行到第15s的时候执行完了,那么客户端1会去释放锁,则此时释放的却是客户端2刚获取到的锁。
这样会有锁过期和释放其他服务锁这种严重的问题,Redisson很好的解决了这个问题。
四.Redisson实现分布式锁
当一个线程1获取锁成功时,Redisson会再开启一个后台线程,每隔一段时间(设置的过期时间的1/3)就会检查当前线程1是否还持有锁,如果持有就会延长锁的时间,此时线程2来获取锁,判断能否加锁成功,失败就会一直尝试加锁,当线程1执行完成后,再释放锁,线程2就可以拿到锁去执行后续任务
具体实施步骤:
- 导入Redisson依赖
- 注册一个Redisson
- 加锁,设置过期时间(
try
) - 释放锁(
finale
)
注: 以上是基于Redis单机,在集群情况下还需结合Lua脚本实现(小编暂时不会,有需要自行百度)