秒杀场景下用Redis分布式锁解决超卖问题

news2025/1/16 1:30:48

前言

超卖问题通常出现在多用户并发操作的情况下,即多个用户尝试购买同一件商品,导致商品库存不足或者超卖。解决超卖问题的方法有很多:乐观锁、Redis分布式锁、消息队列等。
分布式锁是一种多节点共享的同步机制,通过在多个节点之间协调访问资源,确保在同一时间只有一个节点能够获取锁并执行关键操作。在电商网站中,可以将每个商品的库存作为共享资源,使用分布式锁来控制并发访问。

在这里插入图片描述
分布式锁的目的是保证在分布式部署的应用集群中,多个服务在请求同一个方法或者同一个业务操作的情况下,对应业务逻辑只能被一台机器上的一个线程执行,避免出现并发问题。


分布式锁要满足的条件:

● 多进程互斥:同一时刻,只有一个进程可以获取锁
● 保证锁可以释放:任务结束或出现异常,锁一定要释放,避免死锁

● 阻塞锁(可选):获取锁失败时可否重试
● 重入锁(可选):获取锁的代码递归调用时,依然可以获取锁

Redis分布式锁:

在实现Redis分布式锁之前,我们先来看看为什么Redis能实现分布式锁:

  1. 在Redis中,利用Redis的setnx命令,这个命令的特征时如果多次执行,只有第一次执行会成功,可以实现互斥的效果。这满足了多线程互斥的要求
SETNX lock thread1
  1. 在Redis中,利用Redis的del命令,可以删除一个key,即释放锁。
DEL lock
  1. 如果获取锁成功后服务宕机,发生了不释放锁的问题,Redis也可以通过给Key加有效时间,让超时自动释放。这样一来,也满足了保证锁可以释放,达到了分布式锁必须满足的条件!
EXPIRE lock 10  

并且,也不用担心在EXPIRE设置有效期之前服务宕机,Redis的set命令可以满足setnx和expirr的原子性,用一个指令完成两个步骤!

set lock thread1 EX 10 NX

分布式架构中实现

  1. 加Redis坐标
<dependency>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
  1. 配置Redis端口信息
Spring:
	redis:
	    port: 6379
	    host: localhost
  1. 构造加锁工具类
public interface ILock {
    /**
     * 尝试获取锁
     * @param timeoutSec 锁持有的超时时间,过期后自动释放
     * @return true代表获取锁成功; false代表获取锁失败
     */
    boolean tryLock(long timeoutSec);
    /**
     * 释放锁
     */
    void unlock();
}
public class SimpleRedisLock implements ILock  {

    private StringRedisTemplate stringRedisTemplate;

    public SimpleRedisLock(StringRedisTemplate stringRedisTemplate) {
        this.stringRedisTemplate = stringRedisTemplate;
    }

    @Override
    public boolean tryLock(long timeoutSec) {

        Boolean secuss = stringRedisTemplate.opsForValue()
                .setIfAbsent("lock", "thread", timeoutSec, TimeUnit.SECONDS);
        return secuss;
    }

    @Override
    public void unlock() {
        stringRedisTemplate.delete("lock");
    }
}
  1. 在实际秒杀业务代码上加锁、解锁
@PostMapping("/kill")
    public String executeSeckillProduct(int productId,int seckillCount){
        //获取锁对象
        SimpleRedisLock redisLock =new SimpleRedisLock(stringRedisTemplate);
        //尝试获取锁对象
        boolean isLock = redisLock.tryLock(1200);
        if(!isLock){
            return "获取锁失败";
        }
        Product product = productService.findByPid(productId);

        if(product.getStock()<seckillCount){
            return "库存不足";
        }

        product.setStock(product.getStock()-seckillCount);

        int row = productService.reduceInventory(product);

        if(row>0){
            //新增秒杀记录
            SeckillRecord record = new SeckillRecord();
            record.setPid(product.getPid());
            record.setCount(seckillCount);
            record.setPanme(product.getPname());
            record.setTime(new Timestamp(System.currentTimeMillis()));
            recodeService.save(record);
        }
        //释放锁
        redisLock.unlock();
        return "秒杀成功";
    }

测试

在测试Redis分布式锁之前,我先用了GetWay网关同意了API接口,由网关分发路由请求,通过OpenFegin实现通信并做到负载均衡效果,并对秒杀服务做了节点扩展,尽可能的模拟除了分布式架构:
网关配置:

server:
  port: 7000
spring:
  application:
    name: gateway-application
  cloud:
    nacos:
      discovery:
        server-addr: 127.0.0.1:8848
    gateway:
      discovery:
        locator:
          enabled: true
      routes:
        - id: reckill_route
          uri: lb://service-reckill
          order: 1
          predicates:
            - Path=/reckill-serv/**
          filters:
            - StripPrefix=1

先测试不加分布式锁的效果:
数据库库存:10
在这里插入图片描述
秒杀记录:0在这里插入图片描述
运行服务:
在这里插入图片描述
JMeter测试数据:每秒500个请求
在这里插入图片描述
JMeter端口信息:
在这里插入图片描述
测试结果:

库存还剩6个,消耗4个。
在这里插入图片描述
秒杀记录已经一片糊涂了:
在这里插入图片描述


加上分布式锁测试:
测试数据如上。
库存为0:
在这里插入图片描述
秒杀记录:
在这里插入图片描述

节点拓展后的三个秒杀服务:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
三个服务都共同通过负载均衡消费了请求!

但还要注意的一点是,对于锁的把控一定要按时释放,一次的不释放,可能都会导致后续请求无法成功!

成功撒花!


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

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

相关文章

《TCP/IP网络编程》阅读笔记--getsockopt和setsockopt的使用

目录 1--Socket的多种可选项 2--getsocketopt() 3--setsockopt() 4--代码实例 1--Socket的多种可选项 Socket 拥有多种可选项&#xff0c;其可分为 SOL_SOCKET 层&#xff0c;IPPROTO_IP 层和IPPROTO_TCP 层等&#xff0c;一般通过 getsocketopt() 和 setsockopt() 函数进行…

【Cpolar内网穿透】公网SSH远程连接Termux – 电脑使用安卓Termux

目录 前言 1.安装ssh 2.安装cpolar内网穿透 3.远程ssh连接配置 4.公网远程连接 5.固定远程连接地址 前言 使用安卓机跑东西的时候&#xff0c;屏幕太小&#xff0c;有时候操作不习惯。不过我们可以开启ssh&#xff0c;使用电脑PC端SSH远程连接手机termux。 本次教程主要…

C#,《小白学程序》第二十课:大数(BigInteger)的四则运算之一,加法

大数的&#xff08;加减乘除&#xff09;四则运算、阶乘运算。 乘法计算包括小学生算法、Karatsuba和Toom-Cook3算法。 重复了部分 19 课的代码。 1 文本格式 using System; using System.Linq; using System.Text; using System.Collections.Generic; /// <summary>…

软件架构设计(十一) 软件产品线

1、基本概念 软件产品线是从DSSA(特定领域架构)扩展而来的,我们从事软件开发,当您有了多年的经验之后,往往会沉淀到某一个特定领域,然后做这个领域的行业软件。 此时,您会把一些共性的东西开发出来,稳定下来。如果以后再开发同类型的系统时,做一些小修小改或者应用层…

《Python趣味工具》——自制emoji(1)绘制爱心应援牌❤️

项目目标&#xff1a; 本项目旨在学习如何自制emoji&#xff0c;学习内容主要分为3个方面&#xff0c;分3次来讲&#xff0c;根本在于了解了turtle的使用&#xff01; 1.完成一个爱心应援牌 2.完成静态的emoji 3.让emoji动起来&#xff01; 今天我们来学习第一个部分。 文章目录…

推荐一个图像生成开源项目——Fooocus

目录 什么是Fooocus&#xff1f; 项目地址 性能消耗 如何安装 效果对比 总结 什么是Fooocus&#xff1f; Fooocus是一款图像生成软件&#xff0c;但它不同寻常&#xff0c;是对稳定扩散&#xff08;Stable Diffusion&#xff09;和Midjourney的设计理念的巧妙重新思考。本…

C语言经典100例题(42)--学习使用auto定义变量的用法

目录 题目 问题分析 代码 运行结果 题目 学习使用auto定义变量的用法 问题分析 auto存储类型只对属于块的变量有效。auto变量具有自动存储期限、块作用域&#xff0c;并且无连接。auto存储类型几乎从来不用明确地指明&#xff0c;因为对于在块内部声明的变量&#xff0c;…

超经典 Linux 运维巡检脚本及示例

inux 系统日常巡检脚本&#xff0c;巡检内容包含了&#xff1a;磁盘、内存、CPU、进程、文件更改、用户登录等一系列的操作&#xff0c;直接用就行了。 报告以邮件发送到邮箱&#xff0c;在 log 下生成巡检报告。 一、巡检脚本 #!/bin/bash # Author: HanWei # Date: 2020-…

Nginx__高级进阶篇之LNMP动态网站环境部署

动态网站和LNMP&#xff08;LinuxNginxMySQLPHP&#xff09;都是用于建立和运行 web 应用程序的技术。 动态网站是通过服务器端脚本语言&#xff08;如 PHP、Python、Ruby等&#xff09;动态生成网页内容的网站。通过这种方式&#xff0c;动态网站可以根据用户的不同请求生成不…

Sentinel-限流降级

0.目录 初识Sentinel 流量控制 隔离和降级 授权规则 规则持久化 1.初识Sentinel 1.0目录 雪崩问题及解决方案 服务保护技术对比 Sentinel介绍和安装 微服务整合Sentinel 1.1 雪崩问题 1.1.1 引起雪崩的原本 如果服务D出现了问题&#xff0c;那么当服务A调用服务D时&…

Jetpack Compose 1.5 发布:全新 Modifier 系统带来性能大幅提升

不久前 Compose 1.5.0 稳定版发布&#xff0c;在组合的性能方面得到明显改善&#xff0c;这主要归功于对 Modifier API 的持续重构。 Modifier 是 Compose 中的重要概念&#xff0c;为 Composition 中的 LayoutNode 配置各种样式信息以用于后续渲染。在 1.3.0 之前的 Modifier …

【教师节特辑】做个教师节快乐照片墙吧

写作原因&#xff1a; 教师节到了&#xff0c;身边或多或少都有很多不少的老师&#xff0c;基本以前认识的老师都不记得了&#xff0c;以后总也会认识一些日本老师的。程序员&#xff0c;就应该以自己的方式来庆祝教师节。想了下&#xff0c;要不还是做个照片墙把。 项目链接 …

2024年java面试--mysql(2)

系列文章目录 2024年java面试&#xff08;一&#xff09;–spring篇2024年java面试&#xff08;二&#xff09;–spring篇2024年java面试&#xff08;三&#xff09;–spring篇2024年java面试&#xff08;四&#xff09;–spring篇2024年java面试–集合篇2024年java面试–redi…

电商API与电商数据经济的产生【电商平台-淘宝/京东/拼多多下的API数据经济】

计算机连接了互联网后&#xff0c;释放出了巨大的创新力和价值&#xff0c;同样地&#xff0c;智能合约一旦连接到快速增长的链下数据和API经济&#xff0c;也将变得无比强大。如果智能合约可以连接至链下数据提供商、web API、企业系统、云服务商、物联网设备、支付系统以及其…

高分三号1米分辨率飞机检测识别数据集

二、背景介绍 合成孔径雷达(Synthetic Aperture Radar, SAR) 是一种主动式的微波成像系统&#xff0c;它不受光照、云雾 和气候等自然条件影响&#xff0c;具备全天时、全天候对地 观测的能力&#xff0c;已成为遥感领域重要的信息获取平 台。近年来&#xff0c;随着遥感成像技…

Redis主从复制集群的介绍及搭建

在现代的软件开发中&#xff0c;数据的可靠性和可用性是至关重要的。Redis&#xff0c;作为一个开源的、内存中的数据结构存储系统&#xff0c;以其出色的性能和灵活的数据结构&#xff0c;赢得了开发者们的广泛喜爱。而 Redis 的主从复制功能&#xff0c;更是为我们提供了一种…

基于AHP模型指标权重分析python整理

一 背景介绍 日常会有很多定量分析的场景&#xff0c;然而也会有一些定性分析的场景针对定性分析的场景&#xff0c;预测者只能通过主观判断分析能力来推断事物的性质和发展趋势然而针对个人的直觉和虽然能够有一定的协助判断效果&#xff0c;但是很难量化到指标做后期的复用 …

Mybatis学习笔记2 增删改查及核心配置文件详解

Mybatis学习笔记1 Mybatis入门_biubiubiu0706的博客-CSDN博客 将Mybatis进行封装 SqlSessionUtil工具类 package com.example.util;import org.apache.ibatis.io.Resources; import org.apache.ibatis.session.SqlSession; import org.apache.ibatis.session.SqlSessionFacto…

modinfo对比内核版本号

加载内核&#xff0c;出现版本不一样 cat /proc/verison查看内核板本 模块版本&#xff1a;显示模块的版本号。 $ modinfo [OPTIONS] [MODULE] 参数说明-F, --field <field>: 指定要显示的字段&#xff0c;可以使用逗号分隔多个字段。-k, --kernel <kernel>: 指定…

第15章_瑞萨MCU零基础入门系列教程之Common I2C总线模块

本教程基于韦东山百问网出的 DShanMCU-RA6M5开发板 进行编写&#xff0c;需要的同学可以在这里获取&#xff1a; https://item.taobao.com/item.htm?id728461040949 配套资料获取&#xff1a;https://renesas-docs.100ask.net 瑞萨MCU零基础入门系列教程汇总&#xff1a; ht…