分布式锁解决方案_Redis实现的分布式锁原理

news2024/11/13 11:00:34

 获取锁

互斥:确保只有一个线程获得锁

添加锁 利用setnx的互斥性

启动Redis的我们进行测试看看怎么能某个数据来获得锁

 setnx命令表示上锁只要是aa被创建出来我们不删除aa或者设定时间自动删除,那么这把锁就不能被释放开

释放锁

手动释放锁

 如果我们的某台服务抢到锁了但是该服务挂机了,这个时候其他服务将不能在抢到锁,形成了死锁,这个时候我们可以通过设置超级时间进行

超时释放:获取锁时设置一个超时时间

 

但是还是有个问题我们虽然设置了过期时间让他自动过期,但是如果设置的过期时间还没有到突然又挂机了这个时候我们的锁又称死锁了,那么我们可以使用超时释放操作

超时释放

两步合成一步    ex  秒   px毫秒      ttl  看过期事件

 

 

分布式锁解决方案_Redis实现的分布式锁

在项目的pom.xml中引入依赖

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>

修改service 接口ITOrderService

package com.ss.demo.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.ss.demo.domain.TOrder;
/**
 * <p>
 *  服务类
 * </p>
 */
public interface ITOrderService extends IService<TOrder> {
    /**
     * 创建订单方法
     * @param productId
     * @param count
     * @return
     */
    String createOrder(Integer productId, Integer count);

    /**
     * 使用悲观锁进行实现
     * @param productId
     * @param count
     * @return
     */
    String createOrderPessimisticlock(Integer productId, Integer count);

    /**
     * 乐观锁
     * @param productId
     * @param count
     * @return
     */
    String createOrderOptmisticlock(Integer productId, Integer count);

    /**
     * Redis操作
     * @param productId
     * @param count
     * @return
     */
    String createOrderRedis(Integer productId, Integer count);
}

实现类:TOrderServiceImpl

@Autowired
private StringRedisTemplate redisTemplate;
/**
 * Redis操作
 * @param productId
 * @param count
 * @return
 */
@Transactional
@Override
public String createOrderRedis(Integer productId, Integer count) {

    String key = "lock:";
    Boolean result = redisTemplate.opsForValue().setIfAbsent(key + productId, Thread.currentThread().getId() + "", 5, TimeUnit.SECONDS);
    //如果说我能获得所,也就是为true
    if(!result) {
        return "不准许重复下单";
    }
    try {
        //根据商品id获取商品信息
        Product product = productMapper.selectById(productId);
        if(product == null) {
            throw new RuntimeException("购买商品不存在");
        }
        log.info(Thread.currentThread().getName() +"库存数量" + product.getCount());
        //校验库存
        if(count > product.getCount()) {
            throw new RuntimeException("库存不足");
        }
        //更新库存
        Integer iCount = product.getCount() - count;
        product.setCount(iCount);
        //更新操作
        productMapper.updateById(product);
        //创建订单操作
        TOrder order = new TOrder();
        order.setOrderStatus(1);
        order.setReceiverName("张三");
        order.setReceiverMobile("12345678765");
        //设置订单价格【商品单价*商品数量】
        order.setOrderAmount(product.getPrice().multiply(new BigDecimal(count)));
        orderMapper.insert(order);  //插入订单操作
        //创建订单商品表的操作
        OrderItem orderItem = new OrderItem();
        orderItem.setOrderId(order.getId());     //订单Id
        orderItem.setProduceId(product.getId());  //商品Id
        orderItem.setPurchasePrice(product.getPrice()); //购买价格
        orderItem.setPurchaseNum(count);   //购买数量
        orderItemMapper.insert(orderItem);
        return order.getId();
    }catch (Exception e) {
        e.printStackTrace();
    } finally {
        redisTemplate.delete(key + productId);
    }
    return "创建失败";
}

修改controller:TOrderController

package com.ss.demo.controller;
import com.ss.demo.service.ITOrderService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.AutoConfigureOrder;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
 * <p>
 *  前端控制器
 * </p>
 */
@RestController
@RequestMapping("/order")
public class TOrderController {
    @Autowired
    private ITOrderService orderService;

    @PostMapping("/create")
    public String createOrder(Integer productId, Integer count) {
        //return orderService.createOrder(productId, count);
        return orderService.createOrderRedis(productId,count);
    }
}

启动服务9091,9090

使用Jmeter记性测试

把数据库所有数据复原

 

 

 

 分布式锁解决方案_Redis实现的分布式锁原理【这种问题是在极端情况下可能会出现】

         

Redis分布式锁误删除问题解决方案

设置超时时间远大于业务执行时间,但是会带来性能问题

删除锁的时候要判断,是不是自己的,如果是再删除

修改service实现类:TorderServiceImpl

@Autowired
private RedisTemplate<String ,Object> redisTemplate;

/**
 * Redis操作
 * @param productId
 * @param count
 * @return
 */
@Transactional
@Override
public String createOrderRedis(Integer productId, Integer count) {

    String key = "lock:";
    Boolean result = redisTemplate.opsForValue().setIfAbsent(key + productId, Thread.currentThread().getId() + "", 10, TimeUnit.SECONDS);
    //如果说我能获得所,也就是为true
    if(!result) {
        return "不准许重复下单";
    }
    try {
        //根据商品id获取商品信息
        Product product = productMapper.selectById(productId);
        if(product == null) {
            throw new RuntimeException("购买商品不存在");
        }
        log.info(Thread.currentThread().getName() +"库存数量" + product.getCount());
        //校验库存
        if(count > product.getCount()) {
            throw new RuntimeException("库存不足");
        }
        //更新库存
        product.setCount(product.getCount() - count);
        //更新操作
        productMapper.updateById(product);
        //创建订单操作
        TOrder order = new TOrder();
        order.setOrderStatus(1);
        order.setReceiverName("张三");
        order.setReceiverMobile("12345678765");
        //设置订单价格【商品单价*商品数量】
        order.setOrderAmount(product.getPrice().multiply(new BigDecimal(count)));
        orderMapper.insert(order);  //插入订单操作
        //创建订单商品表的操作
        OrderItem orderItem = new OrderItem();
        orderItem.setOrderId(order.getId());     //订单Id
        orderItem.setProduceId(product.getId());  //商品Id
        orderItem.setPurchasePrice(product.getPrice()); //购买价格
        orderItem.setPurchaseNum(count);   //购买数量
        orderItemMapper.insert(orderItem);
        return order.getId();
    }catch (Exception e) {
        e.printStackTrace();
    } finally {
        //获得我们线程的Id也就是我们的表示
        Integer lockId= (Integer) redisTemplate.opsForValue().get(key + productId);
        //获取当前线程Id
        String threadId = Thread.currentThread().getId() + "";
        //判断表示是否一致
        if(lockId.equals(threadId)) {
            //如果一致则删除
            redisTemplate.delete(key + productId);
        }
    }
    return "创建失败";
}

启动服务9091,9090

使用Jmeter记性测试

把数据库所有数据复原

 

 

分布式锁解决方案_Redis分布式锁不可重入问题

Setnx在极端情况下会出现以下问题

 

不可重入:在同一个线程中,无法多次获得同一把锁

不可重试:锁只能获取一次,不能重试

超时释放:虽然可以防止死锁,但是因为业务执行失效件特别长也会存在锁的释放有安全问题

主从一致性:主从同步回存在延时特性,造成主机从机数据不一致,或者主机宕机后主机和从机数据不一致

 

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

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

相关文章

Ubuntu20.04安装VTK8.2

Ubuntu20.04安装VTK8.2 相关依赖的安装安装步骤参考文献相关依赖的安装 sudo apt install checkinstall sudo apt-get install cmake-curses-gui sudo apt-get install freeglut3-dev sudo apt install libqt5x11extras5-dev sudo apt install cmake-qt-gui安装步骤 1、从官网…

【运筹优化】元启发式算法详解:禁忌搜索算法(Tabu Search,TS)+ 案例讲解代码实战

文章目录 一、介绍二、The Classical Vehicle Routing Problem 经典的车辆路径问题三、基本概念3.1 历史背景3.2 禁忌搜索3.3 搜索空间和邻域结构3.4 Tabus 禁忌3.5 解禁准则3.6 简单禁忌搜索的模板3.7 终止标准3.8 概率TS和候选列表 四、中级概念4.1 搜索强化4.2 多样化4.3 允…

Maven基础使用

Maven 学习目标 理解Maven的用途掌握Maven的基本操作掌握Maven如何创建Web项目 Maven是什么 面临问题 在学习Maven之前&#xff0c;我们先来看一下我们现在做的项目都有哪些问题。假设你现在做了一个crm的系统&#xff0c;项目中肯定要用到一些jar包&#xff0c;比如说myb…

opencv_c++学习(九)

一、图像二值化 固定阈值二值化 threshold ( lnputArray src, outputArray dst,double thresh, double maxvall, int typesrc:待二值化的图像&#xff0c;图像只能是CV_8U和CV_32F两种数据类型。对于图像通道数目的要求和选择的二值化方法相关。 dst:二值化后的图像&#xff…

【数据结构】哈希表上——开放寻址法

文章目录 前言映射哈希冲突开放寻址法思路分析结构分析函数实现插入删除寻找 结语 前言 大家好久不见&#xff0c;今天来讲解一下哈希表的基本原理并使用开放寻址法实现简单哈希表。 映射 哈希表的实现思路就是将一组数据映射成另外一组可以直接查找的数据&#xff0c;假如有…

【LeetCode】每日一题:链表部分经典题型

文章目录 1.反转链表2.链表的中间节点3.合并两个有序链表4.相交链表5.环形链表6.环形链表Ⅱ ​&#x1f47b;内容专栏&#xff1a;《LeetCode刷题专栏》 &#x1f428;本文概括&#xff1a;归纳链表部分经典题型。206.反转链表、876.链表的中间节点、21.合并两个有序链表、160.…

如何优雅地彻底解决 antd 全局样式问题

背景 由于某些原因&#xff0c;我们团队负责在组件 上做二次开发&#xff0c;简单理解就是封装组件&#xff0c;组件库选择了 antd&#xff0c;尴尬的是引入之后发现&#xff0c;父组件 自身是带一套全局样式的&#xff0c;而 子组件antd 又带了一套全局样式&#xff0c;导致 子…

字节原来这么容易进,是面试官放水,还是公司实在是太缺人?

本人211非科班&#xff0c;之前在字节和腾讯实习过&#xff0c;这次其实没抱着什么特别大的希望投递&#xff0c;没想到字节可以再给我一次机会&#xff0c;还是挺开心的。 本来以为有个机会就不错啦&#xff01;没想到能成功上岸&#xff0c;在这里要特别感谢帮我内推的同学&…

《程序员面试金典(第6版)》面试题 16.24. 数对和

题目描述 设计一个算法&#xff0c;找出数组中两数之和为指定值的所有整数对。一个数只能属于一个数对。 题目传送门 示例 1: 输入: nums [5,6,5], target 11 输出: [[5,6]]示例 2: 输入: nums [5,6,5,6], target 11 输出: [[5,6],[5,6]]提示&#xff1a; nums.length &…

基于物联感知和GNSS技术的铁塔安全监测解决方案

监测背景 电力铁塔是承载电力供应的重要设施&#xff0c;它的安全性需要得到可靠的保障。但是铁塔一般安装在户外&#xff0c;分布广泛&#xff0c;且有很多安装在偏远地区&#xff0c;容易受到自然、人力的影响和破环。因此需要使用辅助的方法实时监控通信塔的安全状态&#x…

5th-Generation Mobile Communication Technology(二)

目录 一、5G/NR 1、 快速参考&#xff08;Quick Reference&#xff09; 2、5G Success 3、5G Challenges 4、Qualcomm Videos 二、PHY and Protocol 1、Frame Structure 2、Numerology 3、Waveform 4、Frequency Band 5、BWP 6、Synchronization 7、Beam Management 8、CSI Fra…

一文4000字从0到1用WebDriver+Selenium实现浏览器自动化

前言 Selenium是一款可以自动化操作浏览器的开源项目&#xff0c;最初的目的是浏览器功能的自动化测试&#xff0c;但是随着项目的发展&#xff0c;人们根据它的特性也用来做一些更多的有意思的功能而不仅仅是UI的自动化测试工具。就像Selenium官方网站上描述的那样&#xff0…

chatgpt怎么搭建,以及怎么接入企业微信工作台

gpt目前也用了一段时间了&#xff0c;用起来只能说越来越顺手&#xff0c;然后集成到企业微信让公司全部成员都用起来了。 使用界面如下&#xff1a; 主界面 功能&#xff1a; 1、通过企业微信认证后访问使用&#xff0c;防止非公司人员入侵 2、记录用户姓名和提问内容&#x…

IOS证书制作教程

转载&#xff1a;IOS证书制作教程 点击苹果证书 按钮 点击新增 输入证书密码&#xff0c;名称 这个密码不是账号密码&#xff0c;而是一个保护证书的密码&#xff0c;是p12文件的密码&#xff0c;此密码设置后没有其他地方可以找到&#xff0c;忘记了只能删除证书重新制作&am…

05-函数

函数的定义 函数名 函数名的后面有个圆换号()&#xff0c;代表这个为函数&#xff0c;不是普通的变量名。 形参 在定义函数时指定的形参&#xff0c;在未出现函数调用时&#xff0c;它们并不占内存中的存储单元&#xff0c;因此称它们是形式参数或虚拟参数&#xff0c;简称…

.Net6 使用aspose.cells23.5.0

一、测试代码 internal class Program { static void Main(string[] args) { WorkbookDesigner wb new WorkbookDesigner(new Workbook()); var style new CellsFactory().CreateStyle(); style.Borders.SetColor(C…

LED显示屏控制系统分类

LED显示屏的控制系统可以根据不同的特点和功能进行分类。以下是常见的LED显示屏控制系统分类&#xff1a; 同步控制系统&#xff1a;同步控制系统通过传输同步信号来控制LED显示屏&#xff0c;确保多个显示屏之间的内容同步显示。同步控制系统适用于大型LED显示屏&#xff0c;如…

通过chatGPT学习:kubernetes中的list-watch机制介绍

1、 请解释一下&#xff0c;在kubernetes中的list-watch机制&#xff1f; Kubernetes是一个开源的容器编排和管理系统&#xff0c;它可以有效地管理大规模的容器化应用程序。 在Kubernetes中&#xff0c;list-watch机制是一种重要的机制&#xff0c;用于监视资源的变化并及时…

k8s系列(六)——Service服务发现

Service概述 为什么要使用Service Kubernetes Pod是平凡的&#xff0c;由Deployment等控制器管理的Pod对象都是有生命周期的&#xff0c;它们会被创建&#xff0c;也会意外挂掉。虽然它们可以由控制器自动重建或者滚动更新&#xff0c;但是重建或更新之后的Pod对象的IP地址等都…

什么是单点登录

一、什么是单点登录&#xff1f; 单点登录的英文名叫做&#xff1a;Single Sign On&#xff08;简称SSO&#xff09;。 在初学/以前的时候&#xff0c;一般我们就单系统&#xff0c;所有的功能都在同一个系统上。 后来&#xff0c;我们为了合理利用资源和降低耦合性&#xff…