分布式锁总结
1> redis处理过期key的方式:
redis 当key过期的时候,可能会采取策略来清除掉key. 有以下三种方式: 立即清除(设置过期时间的时候,设置一下回调,当时间过期,直接删除掉key) 惰性清除(key过期的时候不会立即去清除掉key,等下次取数据的时候,判断是否过期,过期了就会删除掉key). 定时清除. redis使用的是 定时清除和惰性清除 两种策略.
关于分布式锁:
在日常开发中,如果是单应用实例,为了防止并发所导致的一系列的问题,我们可以使用java中提供的那些锁机制来保障( synchronized或者reentrantlock). 但是在分布式环境中,由于会存在多个jvm进程,导致锁对象不唯一,这样java中提供的那些锁机制就没有用了,这时候我们就需要寻求分布式锁来保障 并发带来的问题.
分布式锁是用于解决分布式系统中操作共享资源时的数据一致性问题。
分布式锁需要注意一些问题:
互斥: 分布式系统中运行着多个节点,必须确保在同一时刻只能有一个节点的一个线程获得锁。
死锁: 分布式系统中如果一个节点获取到锁挂掉,锁一直不能释放,导致其他节点处于阻塞状态,所以有必要设置时效,无论什么状态,锁都能释放.
性能: 对于访问量大的共享资源,如果获取到锁会造成长时间的等待,导致大量节点阻塞,不可以.尽量减少等待时间(业务执行时间以及及时通知)
重入: 锁是基于线程的分配.
阻塞性: 根据实际业务场景来判断,当没有获取到锁的时候,采取阻塞等待还是直接返回.
多个线程并发执行一个方法场景.
1>>>>>如果该方法里面全部是操作数据库的一些东西, 可以直接使用数据库的事务并发机制来控制.不需要其他的分布式锁来保障.(单业务节点也可以使用)
一般存在repeatable read隔离级别下,对数据的读取,修改事务提交操作.(repeatable隔离级别下加锁机制,可能会死锁,用更新锁来解决)
2>>>>>如果是为了防止多个线程执行同一方法导致数据不一致性,可以使用分布式锁.
分布式锁的实现方式:
1>基于数据库来实现. 新建一张表: id method_name 建立method_name唯一索引,由数据库来保障. 执行方法前,先必须获取到锁,insert into tb (method_name) value(method_name) 返回成功就获取到锁,没有成功就没有获取到锁,执行完的时候 删除掉这条数据,让其他线程可以开始去处理.
缺点: 数据库单点故障,万一表挂了,则整个出错. 非重入性 没有过期时间,万一内部崩溃则锁一直无法释放 非阻塞
改进:使用select.....for update 有效避免非阻塞 但是可能会锁表.
3>基于zookeeper实现分布式锁
https://www.cnblogs.com/austinspark-jessylu/p/8043726.html
https://www.cnblogs.com/linjiqin/p/8003838.html
2>基于redis缓存来实现分布式锁
注意点: 2.1> redis加锁的时候 setnx 和 设置有效期不能拆开,因为不是一个原子操作.正确使用
String result = jedis.set(lockKey, requestId, nx=Ture, px=5);
2.2> 删除锁的时候要注意:判断一下当前对象和持有锁的对象是否一致,避免释放掉其他线程的锁.(根据不同业务来选择是否需要判断)
实现的几种方式:直接上代码
1> set方式:
while(jedis.exists(lock)){
Thread.sleep(500);
}
set(lock,1);
执行业务代码;
jedis.del(lock);
2> setnx 方式:
while(jedis.setnx(lock,1)==0){
Thread.sleep(300);
}
执行业务代码;
jedis.del(lock);
setnx不直接设置timeout:
while(jedis.setnx(lock,now+超时时间)==0){
if(jedis.get(lock)<now){
jedis.del(lock);
jedis.setnx(lock,now+超时时间);
break;
}else{
Thread.sleep(300);
}}
执行业务代码;
jedis.del(lock);
while(jedis.setnx(lock, now+超时时间)==0){
if(now>jedis.get(lock) && now>jedis.getset(lock, now+超时时间)){
break;
}else{
Thread.sleep(300);
}
}
执行业务代码;
jedis.del(lock);
redis中存在一种 set 直接设置nx px(过期时间) 让判断过期时间的工作扔给redis更高效
目前python中改造采用:
while not cls.get_lock_rs().set(sender_rs_key, 1, nx=True, px=5): #setnx+timeout 如果没有获取到锁
time.sleep(0.005) #sleep 5ms
执行业务代码;
del(key)
考虑一个问题:针对python部分的锁方式,如果保证锁的正确释放(A线程不能释放B线程的锁),可以采用value=thread_id del的时候进行判断
对于java中的分布式锁服务 采用redisson来实现. redisson是对jedis的上层封装,提供分布式集合 和分布式锁.
主要看一下分布式锁:
使用方式: 可以以redis单节点或者集群搭建不同的redisson连接对象.使用集群可以避免redis单节点故障.
java中的锁具有重入性 unlock 可以判断是否是同一个线程.
python调用要注意: 超时时间 不同的线程id问题.