首页 > 数据库 >【Redis场景5】集群秒杀优化-分布式锁

【Redis场景5】集群秒杀优化-分布式锁

时间:2023-03-04 17:45:33浏览次数:43  
标签:Redis 互斥 PREFIX 秒杀 threadId 线程 分布式

集群环境下的秒杀问题

前序

【Redis场景1】用户登录注册

【Redis场景2】缓存更新策略(双写一致)

【Redis场景3】缓存穿透、击穿问题

【Redis场景拓展】秒杀问题-全局唯一ID生成策略

【Redis场景4】单机环境下秒杀问题


在单机环境下的并发问题,我们可以使用相关锁来解决;但是在集群环境中,笔者测试通过Nginx做的反向代理和负载均衡,请求的时候锁会出现失效的问题。

原因:我们部署多个服务(存在多个tomcat服务器),每个tomcat都有一个属于自己的jvm.每个锁在同容器中有效,但是跨容器后就无法实现互斥效果。

引出分布式锁:

  1. 分布式就是指数据和程序可以不位于一个服务器上,而是分散到多个服务器,以网络上分散分布的地理信息数据及受其影响的数据库操作为研究对象的一种理论计算模型。
  2. 分布式锁提供了多个服务器节点访问共享资源互斥的一种手段。

一个最基本的分布式锁需要满足:

  • 互斥 :任意一个时刻,锁只能被一个线程持有;
  • 高可用 :锁服务是高可用的。并且,即使客户端的释放锁的代码逻辑出现问题,锁最终一定还是会被释放,不会影响其他线程对共享资源的访问。
  • 可重入:一个节点获取了锁之后,还可以再次获取锁

分布式锁的实现:

  1. 基于redis中的SETNX 实现分布式锁
  2. 基于Zookeeper的节点唯一性和有序性实现互斥的分布式锁
  3. 基于MySQL本身的互斥锁机制

基于Redis的分布式锁

基本实现

GitHub完整代码:https://github.com/xbhog/hm-dianping/tree/20230211-xbhog-redisCloud

锁接口实现:20230211-xbhog-redisCloud

/**
 * @author xbhog
 * @describe:
 * @date 2023/2/16
 */
public interface ILock {

    boolean tryLock(Long timeOutSec);

    void unLock();
}

加锁解锁实现类:

@Override
public boolean tryLock(Long timeOutSec) {
    String threadId = ID_PREFIX + Thread.currentThread().getId();
    Boolean isLock = stringRedisTemplate.opsForValue().setIfAbsent(KEY_PREFIX + keyName, threadId + "", timeOutSec, TimeUnit.SECONDS);
    //防止拆箱引发空值异常
    return Boolean.TRUE.equals(isLock);
}
@Override
public void unlock() {
    //通过del删除锁
    stringRedisTemplate.delete(KEY_PREFIX + name);
}

锁误删问题

img

现在有两个锁,线程1获取锁时,由于业务的阻塞超时释放了,这是线程2开始操作,获取锁,在线程2执行业务期间,线程1业务在一段时间内不阻塞且业务完成,这是开始执行释放锁的操作,但是这是锁是线程2,由此造成锁的误删问题;

正确流程:

img

解决的方式:

修改之前的分布式锁实现,满足:在获取锁时存入线程标示(可以用UUID表示) 在释放锁时先获取锁中的线程标示,判断是否与当前线程标示一致

  • 如果一致则释放锁
  • 如果不一致则不释放锁

核心逻辑:在存入锁时,放入自己线程的标识,在删除锁时,判断当前这把锁的标识是不是自己存入的,如果是,则进行删除,如果不是,则不进行删除。

处理流程:

img

代码实现:

private static final String ID_PREFIX = UUID.randomUUID().toString(true) + "-";
@Override
public boolean tryLock(Long timeOutSec) {
    String threadId = ID_PREFIX + Thread.currentThread().getId();
    Boolean isLock = stringRedisTemplate.opsForValue().setIfAbsent(KEY_PREFIX + keyName, threadId + "", timeOutSec, TimeUnit.SECONDS);
    //防止拆箱引发空值异常
	return Boolean.TRUE.equals(isLock);
}
@Override
public void unLock() {
    String threadId = ID_PREFIX + Thread.currentThread().getId();
    //获取当前分布式锁中的value
    String id = stringRedisTemplate.opsForValue().get(KEY_PREFIX + keyName);
    //锁相同则删除
    if(threadId.equals(id)){
        stringRedisTemplate.delete(KEY_PREFIX + keyName);
    }

}

标签:Redis,互斥,PREFIX,秒杀,threadId,线程,分布式
From: https://www.cnblogs.com/xbhog/p/17178684.html

相关文章

  • 自实现分布式链路追踪 方案&实践
    前言:排查问题是程序员的基本能力也是必须要会的,在开发环境,我们可以debug,但是一旦到了服务器上,就很难debug了,最有效的方式就是通过日志揪出bug,而一次请求的日志如果没有一个......
  • 分布式系统中的网络分区问题
    网络分区问题什么是网络分区?网络分区只在分布式集群中,节点之间由于网络不通,导致集群中节点形成不同的子集,子集中节点之间网络互通,而子集与子集之间网络不通。如何判断是......
  • redis cluster 部署
    rediscluster部署服务器说明192.168.2.200:7000...192.168.2.200:7005创建集群目录mkdircluster-testcdcluster-testmkdir7000700170027003700470......
  • Windows系统安装Redis服务
    网上有很多的安装教程,但Redis版本都比较老,最近从github上搜索到最新版本,验证可用,记录一下1、应用下载地址:https://github.com/zkteco-home/redis-windows2、选择对应得ta......
  • 【redis】配置优化及从库优先级
    https://blog.51cto.com/u_15902893/5912902 vim/etc/redis/redis.conf##################基础#################################daemonizeyes//是否以守护进程......
  • Redis分布式锁常见坑点分析
    日常开发中,基于Redis天然支持分布式锁,大家在线上分布式项目中都使用过Redis锁。本文主要针对日常开发中加锁过程中某些异常场景进行讲解与分析。本文讲解示例代码都在......
  • 【Redis的三种数据删除策略】定时定期惰性,超出内存就自动清理
    https://blog.csdn.net/DQWERww/article/details/126453008https://blog.csdn.net/qq_38056518/article/details/122107638  内存淘汰机制Redis有过期策略......
  • SequoiaDB分布式数据库2023.2月刊
    本月看点速览技术实力获认可,获评多项荣誉​共建人才生态,与深圳大学举办奖学金颁奖仪式青杉计划2023持续进行,一起攀登更高的“杉”技术实力获认可,获评多项荣誉......
  • Redis 实现唯一全局ID
      packagecom.hmdp.utils;importorg.springframework.beans.factory.annotation.Autowired;importorg.springframework.data.redis.core.StringRedisTemplate;......
  • redis sentinel 部署
    redissentinel部署服务器说明192.168.2.200masterredis-serverredis-sentinel192.168.2.201slave1redis-serverredis-sentinel192.168.2.202slave2r......