Redis分布式锁对消息保证原子性

近段时间做APP的活动预热业务和活动抽奖业务时,涉及Redis对List类型的活动消息数据的插入和取出时,发现如果不保证数据的原子性,会导致数据紊乱,导致用户对抽奖的效果不佳(粘性用户的抽奖效果无法保证,奖品被粘性不高的用户获取,违背活动的意图)

解决办法

redi分布式锁

分布式锁特点

  • 互斥性:同一时间只能有一个节点获取到锁,其他节点需要等待获取到锁的节点释放锁才能获取到锁,一般是通过阻塞和自旋两种方式
  • 安全性:只能释放自己的锁不能误删别人的锁
  • 死锁:在节点宕机时最容易出现锁没有释放是问题,然后出现死锁,所以要做锁的过期
  • 容错:当redis宕机,客户端仍然可以释放锁
  • 可重入:获取锁失败可以尝试获取锁

我主要是解决当活动开始时,大量的用户在redis缓存中取出抽奖消息进行比对,因为没有加锁,比对后没有中奖的用户将令牌从左侧插入,导致令牌桶顺序淆乱,从而影响用户用户的抽奖体验。

纯分布式锁方式:
public class RedissonManager {
  private static Config config = new Config();
  //声明redisso对象
  private static Redisson redisson = null;
  
   //实例化redisson
	static{
	  config.useClusterServers()
	  // 集群状态扫描间隔时间,单位是毫秒
	 .setScanInterval(2000)
	  //cluster方式至少6个节点(3主3从,3主做sharding,3从用来保证主宕机后可以高可用)
	 .addNodeAddress("redis://127.0.0.1:6379" )
	 .addNodeAddress("redis://127.0.0.1:6380")
	 .addNodeAddress("redis://127.0.0.1:6381")
	 .addNodeAddress("redis://127.0.0.1:6382")
	 .addNodeAddress("redis://127.0.0.1:6383")
	 .addNodeAddress("redis://127.0.0.1:6384");
	 
	  //得到redisson对象
	  redisson = (Redisson) Redisson.create(config);
	}
	
	  //获取redisson对象的方法
	  public static Redisson getRedisson(){
	    return redisson;
	 }
}

// 获取锁
RLock rLock = redissonClient.getLock("lock_stock");

// 加锁并设置有效期
rLock.lock(""RDL",200")

//释放锁
rLock.unlock("RDL");

redisson官方文档对分布式锁的解释:

  • Redisson加锁自动过期时间30秒,监控锁的看门狗发现业务没有执行完,会自动进行锁的续期,这样的好处是防止在程序执行期间锁自动过期被删除的问题
  • 当业务完成不再给锁续期,即使没有手动释放锁,锁的过期时间到了也会在自动释放锁

红锁

 Redis常用的方式有单节点、主从模式、哨兵模式、集群模式,在后三种模式中可能会出现 ,异步数据丢失,脑裂问题,Redis官方提供了解决方RedLock,RedLock是基于redis实现的分布式

  • 红锁的特点
    • 容错性:只要大多数的节点的redis实例正常运行就能对外提供服务,释放锁
    • 互斥性:只要有一个客户端能获取锁,即使发生客户端宕机,也不会发生死锁
基于Redis的Redisson红锁RedissonRedLock对象实现RedLock介绍的加锁算法,该对象可以用来将多个RLock对象关联成一个红锁,每个RLock对象实例可以来自不同发Redisson实例

RLock lock1 = redissonInstance1.getLock("lock1");
RLock lock2 = redissonInstance2.getLock("lock2");
RLock lock3 = redissonInstance3.getLock("lock3");

RedissonRed;ock lock = new RedissonRedLock(lock1,lock2,lock3);
// 同时加锁lock1,lock2,lock3
// 红锁在部分节点成功就算成功
(1)
lock.lock();
...
lock.unloxk();
(2)通过leaseTime的参数来指定加锁时间,超过时间自动解锁 
lock.lock(10,TimeUnit.SECNDES);
// 为加锁等待100秒针,并在加锁成功10秒自动解开
boolean res = lock.trylock(100,10,TimeUnit.SECNDES);
...
lock.unlock();

posted on 2024-06-19 17:41  强K  阅读(1)  评论(0编辑  收藏  举报