redis里面的分布式锁的作用和分布式锁的实现
分布式锁在 Redis 中的作用
在分布式系统中,多个服务实例或进程可能会同时访问和操作共享资源,若缺乏有效的同步机制,就会引发数据不一致、并发冲突等问题。
1、保证数据一致性:在分布式环境下,多个客户端可能同时对同一数据进行读写操作。使用 Redis 分布式锁可以确保同一时间只有一个客户端能够修改数据,避免出现数据冲突和不一致的情况。例如,在电商系统中,多个订单处理服务可能同时处理库存扣减操作,如果不使用分布式锁,可能会导致库存超卖的问题。通过使用 Redis 分布式锁,只有获取到锁的服务才能进行库存扣减,从而保证库存数据的准确性。
2、避免并发问题:多个客户端可能会同时执行相同的业务逻辑,如定时任务、数据更新等。Redis 分布式锁可以避免这些任务在不同的节点上同时执行,从而避免并发问题。例如,在分布式系统中,每天凌晨需要对数据进行备份操作,如果不使用分布式锁,可能会导致多个节点同时进行备份,造成资源浪费和数据混乱。通过使用 Redis 分布式锁,只有一个节点能够获取到锁并执行备份任务,其他节点则等待锁释放。
3、提高系统可靠性:Redis 是一个高性能、高可用的内存数据库,使用 Redis 作为分布式锁的存储介质可以提高系统的可靠性。Redis 支持主从复制和集群模式,可以在节点故障时自动进行故障转移,确保分布式锁的正常使用。
分布式锁的实现方式
1、SETNX + EXPIRE:setnx+ expire命令。即先用setnx来抢锁,如果抢到之后,再用expire给锁设置一个过期时间,防止锁忘记了释放。
if(jedis.setnx(key_resource_id,lock_value) == 1){ //加锁
expire(key_resource_id,100); //设置过期时间
try {
do something //业务请求
}catch(){
}
finally {
jedis.del(key_resource_id); //释放锁
}
}
2、SETNX + value值是(系统时间+过期时间):解决方案一,发生异常锁得不到释放的场景,过期时间放到setnx的value值里面。如果加锁失败,再拿出value值校验一下即可。
long expires = System.currentTimeMillis() + expireTime; //系统时间+设置的过期时间
String expiresStr = String.valueOf(expires);
// 如果当前锁不存在,返回加锁成功
if (jedis.setnx(key_resource_id, expiresStr) == 1) {
return true;
}
// 如果锁已经存在,获取锁的过期时间
String currentValueStr = jedis.get(key_resource_id);
// 如果获取到的过期时间,小于系统当前时间,表示已经过期
if (currentValueStr != null && Long.parseLong(currentValueStr) < System.currentTimeMillis()) {
// 锁已过期,获取上一个锁的过期时间,并设置现在锁的过期时间(不了解redis的getSet命令的小伙伴,可以去官网看下哈)
String oldValueStr = jedis.getSet(key_resource_id, expiresStr);
if (oldValueStr != null && oldValueStr.equals(currentValueStr)) {
// 考虑多线程并发的情况,只有一个线程的设置值和当前值相同,它才可以加锁
return true;
}
}
//其他情况,均返回加锁失败
return false;
}
3、使用Lua脚本(包含SETNX + EXPIRE两条指令):我们还可以使用Lua脚本来保证原子性(包含setnx和expire两条指令)
if redis.call('setnx',KEYS[1],ARGV[1]) == 1 then
redis.call('expire',KEYS[1],ARGV[2])
else
return 0
end;
4、Redisson框架:获得锁的线程,开启一个定时守护线程,每隔一段时间检查锁是否还存在,存在则对锁的过期时间延长,防止锁过期提前释放。(看门狗机制)

浙公网安备 33010602011771号