一、分布式锁
redis实现分布式锁
1、为什么要使用分布式锁?
为保证一个方法或者属性在高并发情况下的同一时间只能被同一个进程执行。
2、什么是分布式锁?
控制分布式系统有序的去对共享资源进行操作,通过互斥来保持一致性。
3、分布式锁的分类和实现方式?
**注意:**分布式的CAP理论告诉我们“任何一个分布式系统都无法同时满足一致性(Consistency)、可用性(Availability)和分区容错性(Partition tolerance),最多只能同时满足两项。”所以,很多系统在设计之初就要对这三者做出取舍‘’。
实现方式:
- 数据库实现分布式锁
- zookeeper实现分布式锁
- redis实现分布式锁
4、分布式锁具备条件:
- 在分布式的系统环境下,一个方法在同一时间只能被一个线程锁执行
- 高可用,高性能获取锁和释放锁
- 具备可重入特征
- 避免死锁
二、如何设计一个分布式锁?如何对锁性能进行优化?
分布式锁本质:就是在所有进程都能访问到的一个地方,设置一个锁资源,让这些资源都来竞争锁资源。
通常对于分布式锁,会要求响应快、性能高、与业务无关。
1、用redis实现分布式锁的好处?
- redis有很高的性能
- redis命令对此支持友好,实现起来方便
基于Redis方式实现分布式锁:
-
SETNX key value: 当key不存在的时候,就将key设置为value,并返回1,如果key存在,就返回0.
-
EXPIRE key locktime: 设置key的过期时间
-
DEL key 删除锁资源
1、最简单的分布式锁: SETNX加锁,DEL解锁
问题: 如果获取到锁的进程执行失败,那么他就永远不会主动解锁,那么这个锁就会被锁死了。
2、给锁设置过期时长
==问题:==SETNX和EXPIRE并不是原子性操作,所以获取到锁的进程有可能还没有执行EXPIRE指令,就挂了,这时锁还是会被锁死。
3.将锁的内容设置为过期时间(客户端时间 + 过期时长),SETNX获取锁失败时,拿这个时间跟当前时间对比,如果是过期时间锁,那就先删除锁,再重新上锁。
==问题:==在高并发的情况下,会产生多个进程同时拿到锁的情况。
4.setNX失败后,获取锁上的时间戳,然后getset,将自己的过期时间更新上去,并获取旧值。如果这个旧值跟之前获得的时间戳是不一样的,就表示这个锁已经被其他进程占用了,自己就要放弃竞争锁。
//基于redis实现分布式锁(效率高)
public boolean tryLock(RedisnConnection conn){
long nowTime = System.currentTimeMillis();
long expireTime = nowTime + 1000;
if(conn.SETNX("mykey",expireTime) == 1){
conn.EXPIRE("mykey",1000);
return true;
}else{
//获得旧值
long oldVal = conn.get("mykey");
if(oldVal != null && oldVal < nowTime){
// 获得当前值
long currentVal = conn.GETSET("mykey",expireTime);
if(oldVal == currentVal){
conn.EXPIRE("mykey",1000);
return true;
}
return false;
}
return false;
}
}