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

news2024/11/20 8:37:28

集群环境下的秒杀问题

前序

【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);
    }

}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/388148.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

39. 实战:基于api接口实现视频解析播放(32接口,窗口化操作,可导出exe,附源码)

目录 前言 目的 思路 代码实现 需要导入的模块 1. 导入解析网站列表,实现解析过程 2. 设计UI界面 3. 设置窗口居中和循环执行 4. 注意事项 完整源码 运行效果 总结 前言 本节将类似34. 实战:基于某api实现歌曲检索与下载(附完整…

SpringCloud:Nacos的安装(Windows,Linux)

目录 一、认识和安装Nacos 1、下载 2、点击进入Github,进入Releases 3、点击Tags 4、解压(Windows版) 5、端口配置 6、启动 7、访问 二、Linux系统安装Nacos 1、打开虚拟机,使用xshell连接虚拟机,Nacos依赖于…

JVM内置锁synchronized关键字详解

目录 JVM内置锁synchronized关键字详解 设计同步器的意义 如何解决线程并发安全问题? synchronized原理详解 synchronized底层原理 synchronized在jdk1.6前后的变化【重点】 jdk小于1.6时 jdk>1.6时 轻量级锁何时升级为重量级锁??…

【ROS学习笔记10】ROS中配置自定义Cpp头文件和导入自定义Python库

【ROS学习笔记10】ROS中配置自定义Cpp头文件和导入自定义Python库 文章目录【ROS学习笔记10】ROS中配置自定义Cpp头文件和导入自定义Python库一、ROS中的头文件和源文件1.1 自定义头文件调用1.2 自定义源文件调用二、Python模块的导入Reference写在前面,本系列笔记参…

springBoot 启动指定配置文件环境多种方案

springBoot 启动指定配置文件环境理论上是有多种方案的,一般都是结合我们的实际业务选择不同的方案,比如,有pom.xml文件指定、maven命令行指定、配置文件指定、启动jar包时指定等方案,今天我们一一分享一下,以供参考&a…

Java知识复习(十二)Docker

1、容器 一句话概括容器:容器就是将软件打包成标准化单元,以用于开发、交付和部署容器镜像是轻量的、可执行的独立软件包 ,包含软件运行所需的所有内容:代码、运行时环境、系统工具、系统库和设置。容器化软件适用于基于 Linux 和…

Redis学习(一):NoSQL概述

为什么要使用Nosql 现在是大数据时代,过大的数据一般的数据库无法进行分析处理了。 单机MySQL的年代 90年代,一个基本的网站访问量一般不会太大,单个数据库完全足够! 那个时候,更多的去使用静态网站,服务器…

TD算法超详细解释,一篇文章看透彻!

【已解决】TD算法超详细解释和实现(Sarsa,n-step Sarsa,Q-learning)一篇文章看透彻! 郑重声明:本系列内容来源 赵世钰(Shiyu Zhao)教授的强化学习数学原理系列,本推文出于非商业目的分享个人学习…

DockerFile创建及案例

DockerFile dockerfile是用来构建docker镜像的文件,命令脚本参数脚本! 构建步骤 编写一个dockerfile文件docker build 构建成为一个对象docker run 运行镜像docker push 发布镜像(DockerHub、阿里云镜像仓库) 去官网Docker-Hub…

51单片机——串口通信,小白讲解,相互学习

通讯的基本概念 51单片机不仅可以实现串口通信,还可以通过IO口模拟实现多种其他通信,比如 SPI,IIC等,学习这些通信前,我们很有必要了解下通信的基本概念。通信的方式可以分为多种,按照数据传输方式可分为串…

MySQL——复合查询+表的内外连接

文章目录复合查询基本查询多表查询自连接子查询1、单行子查询2、多行子查询3、多列子查询4、在from子句中使用子查询😊5、合并查询结果unionunion all表的内连和外连内连接外连接左外连接右外连接复合查询 接下来我们就要进行的是多张表里进行查询操作,…

大数据相关面试题

linux 常见linux高级命令? top、iotopnetstatdf -hjmap -heaptarrpmps -efshell 用过的shell工具? awk Awk 命令详解 - 简书 awk是行处理器: 相比较屏幕处理的优点,在处理庞大文件时不会出现内存溢出或是处理缓慢的问题,通常用来…

MySQL中的Join连接查询

目录JoinJoin的分类笛卡尔积笛卡尔积出现的原因为什么不推荐有笛卡尔积出现那应该怎么做多表连接Join的使用小表驱动大表小表驱动大表是什么小表驱动大表的好处如何区分哪一个是驱动表和被驱动表Join原理及算法NLJ算法BNLJ算法总结:如何写入高性能的连接查询为什么M…

一阶低通滤波介绍及simulink模型

一阶低通滤波 背景介绍 低通滤波是一种过滤方式,规定低频信号能正常通过,而超过设定临界值的高频信号则被阻隔、减弱。低通滤波可以简单的认为:设定一个频率点,当信号频率高于这个频率时不能通过,在数字信号中&#…

对象图实例解析

总目录链接>> AutoSAR入门和实战系列总目录 文章目录更快、更好、更轻松地学习 UML对象图的目的对象图一览类到对象图示例 - 订单系统基本对象图符号和符号类图与对象图对象图 - 通过示例学习对象图示例 I - 公司结构对象图示例 II - POS对象图示例 III - Writer对象结构…

Java中的Comparator 与 Comparable详解

Comparator VS Comparable1. Comparator1.1 对一维数组进行排序1.2 对二维数组进行排序1.3 对对象数组进行排序2. Comparable3. 二者区别1. Comparator 通过源码发现Comparator是一个接口。 根据compare方法中的注释可以发现方法返回三种类型的值,正数、零、负数&a…

4.1 路由器(华硕 官改/梅林 华为 小米 路由) 使用花生壳 实现远程管理

最近添置了一台华硕的八爪鱼GT AC5300,到手后刷了官改,而里面软件中就提供了花生壳程序,想到花生壳为每个用户提供了两条免费映射(带宽为1mbs,流量为1g/月),所以就打算利用来做一个远程访问。具…

开发手册——一、编程规约_7.控制语句

这篇文章主要梳理了在java的实际开发过程中的编程规范问题。本篇文章主要借鉴于《阿里巴巴java开发手册终极版》 下面我们一起来看一下吧。 1. 【强制】在一个 switch 块内,每个 case 要么通过 break / return 等来终止,要么注释说明程序将继续执行到哪…

CSGO服务器配置全贴纸插件方法教程

CSGO服务器配置全贴纸插件方法教程 关于插件的警告 一定要了解V社对于CSGO社区服务器的规定,全皮肤插件/全手套插件等,在设置了GSLT的情况下,是有可能被封禁GSLT账号的(所以慎用) 配置好服务器之后呢,想安…

uniapp+uView2.0实现自定义动态tabbar

1.需求说明 2.实现原理说明 3.实现过程 3.1集成uView2.0 3.2 自定义tabbar 3.3 vuex定义tabbar共享信息 3.4 tabbar显示个数控制 1.需求说明 要求不同时间显示不同的tabbar.点击不同的tabbar跳转到不同的页面,能随时…